Minecraft et les entités
-
Salut !
Je voulais obtenir l’avis de programmeurs avertis sur le fonctionnement des entités dans Minecraft. J’ai observé le code source de Entity.class et j’ai trouvé plusieurs méthodes assez… spécialisées on va dire.
Simple exemple : entity.isInWeb(). Techniquement parlant, ça devrait pas exister un truc pareil non ? Ou encore pire : entity.isBurning() ou entity.isWet(). Je veux dire, si je veux créer un bloc HungerBlock qui fait baisser la faim d’un joueur lorsqu’il marche dessus. Je vais pas créer une méthode entity.isOnHungerBlock() dans mon entité non ? Il faudrait utiliser un truc plus générique. Pourquoi Notch (ou Jeb maintenant…) a-t-il fait des méthodes aussi spécialisées ?
Personnellement, j’aurai plutôt vu une liste d’effets applicable aux entités :
public abstract class Entity{ //Tout le code classique des entités private EffectList effects = new EffectList(this); public void applyEffect(Effect effect){ this.effects.add(effect); } public void removeEffect(Effect effect){ this.effects.remove(effect); } public void clearEffects(){ this.effects.clear(); } }
Et ensuite :
public class EntitySpider extends EntityMob{ //Constructeur quelconque public EntitySpider(){ this.effects.addEffectResistance(new EffectPoison(PoisonType.SPIDER));//L'araignée est invulnérable aux poisons d'araignées this.effects.addEffectResistance(new EffectWeb());//L'araignée n'est pas ralentie par ses toiles this.effects.addVulnerability(new EffectFire());//On dit que l'araignée est vulnérable au feu } }
Après : EffectList implements Iterable etc…
Voilà, je voulais donc savoir ce que vous en pensez !
Amicalement,
EclipseOnFire. -
TOUTES les entités par defaut lorsqu’elles sont dans une toile d’araigné se déplace plus lentement.
Les Spiders sont les seules entité a avoir un comportement spécifique.TOUTES les entités par defaut lorsqu’elles sont en feu, prennent des dégats.
Les Ghast et les Blazes sont les seules entité a avoir un comportement spécifique.isWet, je ne sais pas mais ce doit être plus ou moins la même chose. Ça concerne toutes les entités.
Elles ont tous un comportement par defaut décrit dans la classe Entity. Tu peux surcharger une de ses fonctions pour rendre le comportement de ton entité spécifique. C’est le moteur de jeu qui se charge d’appeler cette methode.Ce moteur de jeu a besoin de savoir lorsqu’il manipule une entité, si celle-ci est en feu ou non (par exemple).
Par exemple : Si entity.isBurning() ? j’affiche des flammes a l’écran. Ou si isBurning() && vulérable au feu ? point de vie –;
La tu nous parles de resistance et de vulnérabilité, pas d’état. Une entité peu être en feu, sans pour autant y être vulnérable. -
C’est pas spécialement spécifique les cas que tu montre
isWet permet de savoir si une entité est sous la pluie ( Ou dans l’eau? ) -> En rapport au feu, ça permet d’éteindre l’entité par exemple, ou dans le cas de l’enderman, d’infliger des dégats. De plus, si tu veux faire quelquechose genre pluie acide, en vérifiant que l’entité est wet, et qu’elle ne touche pas de bloc d’eau ( pour éviter l’eau acide ), tu fait ça avec un simple if, au lieu de devoir faire une méthode de vérification / un if assez long
-
Ah d’accord… Bah si tu préfères, ça marche aussi : entity.addEffect(new EffectFire(entity));
Dans la classe EffectFire : (c’est un exemple)
entity.getModel().addModel(FireModel.class);
Et ensuite, entity.hasEffect(EffectFire.class);
Et puis mon exemple de bloc de faim pourrait s’appliquer à toutes les entités, si elles avaient toutes une jauge de faim. -
Ce qui déjà poserait problème car les items n’aurront jamais de barre de faim
En revanche, avec les LivingEntity, tu peux peut-être utiliser ceci : http://www.minecraftforgefrance.fr/showthread.php?tid=905 pour faire ce que tu veux -
@‘EclipseOnFire’:
Ah d’accord… Bah si tu préfères, ça marche aussi : entity.addEffect(new EffectFire(entity));
Dans la classe EffectFire : (c’est un exemple)
entity.getModel().addModel(FireModel.class);
Et ensuite, entity.hasEffect(EffectFire.class);
Et puis mon exemple de bloc de faim pourrait s’appliquer à toutes les entités, si elles avaient toutes une jauge de faim.nan, il y a que le joueur qui a la barre de faim.
-
Mais les items sont accélérés par la glace, donc ça sert. Et rien n’empêche de faire une liste qui retire les effets inutiles (comme l’empoisonnement d’un item ou la faim d’un item).
-
Je pense qu’il y a une différence entre les effets et les attributs.
Quand un Ghast est en feu, il a l’effet feu, par contre isBurning vaut faux.Ensuite parcourir une collection est plus gourmant qu’utiliser un attribut.
-
@‘Blackout’:
Je pense qu’il y a une différence entre les effets et les attributs.
Quand un Ghast est en feu, il a l’effet feu, par contre isBurning vaut faux.Ensuite parcourir une collection est plus gourmant qu’utiliser un attribut.
C’est sûr, après on peut modifier, je parle simplement du principe. Ca reste également plus modulable que de modifier des méthodes dans Entity.class.
-
Plus dynamique ?
Modulable peut-être, mais dynamique ? qu’est ce que tu appel dynamique ? -
@‘Blackout’:
Plus dynamique ?
Modulable peut-être, mais dynamique ? qu’est ce que tu appel dynamique ?Oui modulable pardon, je corrige ^^.
-
On peut penser que ce soit plus modulable, puisque contrairement a un attribut, ici tu ajoutes les effets lors de l’exécution.
Mais il faut encore que le moteur puisse gérer/prendre en compte les effets que tu lui donne, et ça il est incapable de le faire actuellement. Et ça necessiterai la réécriture de tout le code, ce qui ne vaut pas le coups puisqu’au final, le jeu perdrai en performance.Ici on a un compromis entre la difficulté d’implémentation, la modularité et la rapidité d’éxécution.
-
@‘Blackout’:
On peut penser que ce soit plus modulable, puisque contrairement a un attribut, ici tu ajoutes les effets lors de l’exécution.
Mais il faut encore que le moteur puisse gérer/prendre en compte les effets que tu lui donne, et ça il est incapable de le faire actuellement. Et ça necessiterai la réécriture de tout le code, ce qui ne vaut pas le coups puisqu’au final, le jeu perdrai en performance.Ici on a un compromis entre la difficulté d’implémentation, la modularité et la rapidité d’éxécution.
Donnes-moi un exemple pratique, je ne vois pas pourquoi il faudrait réécrire le manager. ^^
-
Tu sais je travaille sur un jeu un minecraft like, et pour te dire j’ai ajouter les entités et je ne dépasser pas les 5 fps (qui a été résolue :p), donc déjà niveau optimisation minecraft est pas ou top mais pas au plus bas. Ensuite il faut savoir que ÉNORMÉMENT de classe on en commun la classe Entity.
-
Bah la le moteur fait a chaque tick :
if(isBurning()) health–;avec ton système de liste :
D’une ça ne peux pas fonctionner car il y pourrait avoir plusieur fois le même effet
de 2, il faut une instance de l’effet, ce qui demande de le mémorisé a l’avance et de retrouver la référence via un identifiant qui doit être unique. Mais ça sous entend qu’on doit connaitre l’ID, donc ça revient a mettre un attribut, donc la boucle est bouclé.Ce qui faudrai c’est que effet, implemente une classe genre par exemple runnable
dans ce cas la on aurrait a chaque tick :
for(effet e : monTreeSet d’effet) { e.run(); }
Mais du coups il faut qu’il y ait une référence vers l’entité dans l’effet, et l’effet ne peux pas être le même pour toutes les entité.
Donc tu aurrais besoin d’instancier trop d’object, et tu saturerais la mémoire.Après pour avoir faire des applications modulaire, je sais de quoi je parle ^^ C’est pas si simple a expliquer, faut que tu en fasses par toi même pour te rendre compte des limites ^^
-
@‘Blackout’:
Bah la le moteur fait a chaque tick :
if(isBurning()) health–;avec ton système de liste :
D’une ça ne peux pas fonctionner car il y pourrait avoir plusieur fois le même effet
de 2, il faut une instance de l’effet, ce qui demande de le mémorisé a l’avance et de retrouver la référence via un identifiant qui doit être unique. Mais ça sous entend qu’on doit connaitre l’ID, donc ça revient a mettre un attribut, donc la boucle est bouclé.Ce qui faudrai c’est que effet, implemente une classe genre par exemple runnable
dans ce cas la on aurrait a chaque tick :
for(effet e : monTreeSet d’effet) { e.run(); }
Mais du coups il faut qu’il y ait une référence vers l’entité dans l’effet, et l’effet ne peux pas être le même pour toutes les entité.
Donc tu aurrais besoin d’instancier trop d’object, et tu saturerais la mémoire.Après pour avoir faire des applications modulaire, je sais de quoi je parle ^^ C’est pas si simple a expliquer, faut que tu en fasses par toi même pour te rendre compte des limites ^^
Si tu as remarqué ce n’est pas une java.util.List mais une liste crée par mes soins. Je n’ai pas détaillé mais cette liste prend en compte tous les cas de figure :
- Elle se charge du ticking des effets ;
- Elle empêche la duplication d’effets ;
- Elle trie les effets applicables à l’entité et ceux qui ne le sont pas (ces effets sont déclarés dans la classe qui héritera Entity.class)
- Elle supprime les effets dés leur échéance et ajoute les nouveaux effets ;
- Elle purge la liste lors de la mort de l’entité etc…
La classe Effect :
public interface ApplicableEffect{ /** ** Called every tick from the list where the effect has been added. **/ public void onUpdate(ApplicableEffectList list){}//list.getEntity().damage(2); etc… /** ** Called before calling onUpdate(). It's used by the list to know if the effect must be removed. **/ public boolean isObsolete(); }
Voilà. Encore une fois c’est qu’un exemple. On peut toujours améliorer.
Après c’est vrai qu’il faudra ajouter des ID, par exemple, en mettant une HashMap <string, applicableeffect=“”>en interne.
Mais rien n’empêche de faire un système n’utilisant pas les ID :boolean isBurning = entity.getEffectList().hasEffect(EffectBurning.class); boolean isWet = entity.getEffectList().hasEffect(EffectWater.class);
Ça offre également la possibilité d’avoir des effets passifs comme une résistance accrue au poison pour les araignées, une résistance au feu pour les ghasts, une résistance aux explosions pour les creepers, une résistance aux flèches pour les squelettes etc…
Sinon, je vois vraiment pas (à part la gourmandise en mémoire) les inconvénients de ma méthode.</string,>
-
Elle trie les effets applicables à l’entité et ceux qui ne le sont pas (ces effets sont déclarés dans la classe qui héritera Entity.class)
Les effets seront donc des entité, bizzare non ?
Mais rien n’empêche de faire un système n’utilisant pas les ID :
Non impossible, la preuve, dans ton exemple tu utilise des ID, la Class de l’effet.
Ensuite comment fait-on a partir de ton ApplicableEffect pour obtenir une référence vers l’entité ?
-
@‘Blackout’:
Les effets seront donc des entité, bizzare non ?
Ensuite comment fait-on a partir de ton ApplicableEffect pour obtenir une référence vers l’entité ?
Non ils seront déclarés depuis ce constructeur ^^. Par exemple :
public class EntityExample{ public EntityExample(World w){ super(w); this.effects.addInvulnerability(EffectFire.class);//Je déclare mes effets pour l'entité. } //Bla bla bla… }
Je me suis mal exprimé, désolé ^^.
Ah je prenais pas les Class comme des ID… D’accord, dans ce cas, oui.
Depuis un effet, on ne peut pas obtenir l’entité. Si, en revanche on est à l’intérieur de l’effet, on peut.
Par exemple :public class EffectFire implements ApplicableEffect, ApplicableEffectListener{ private int ticks; public EffectFire(ApplicableEffectList list, int duration){ if(duration <= 0){ throw new IllegalArgumentException("Duration must be > 0 !"); } list.addListener(this); this.ticks = duration; } @Override public void onUpdate(ApplicableEffectList list){ --this.ticks; if(this.ticks % 20 == 0){ Entity target = list.getEntity(); target.damage(1); } } @Override public boolean isObsolete(){ return this.ticks <= 0; } @Override public void effectAdded(ApplicableEffect effect){ if(effect instanceof EffectWater){ this.ticks = 0; } } }
J’ai écrit la classe EffectFire en entier du coup On peut aussi retirer le Listener et mettre un check dans la méthode onUpdate() :
if(list.hasEffect(EffetWater.class)){
this.ticks = 0;
return;
}