[JAVA] Non-physique similaire utilisé pour exprimer le baseball sur Minecraft et mise en œuvre dans Spigot (lancer / frapper)

Autres articles

Lié Catching / Tips

tangage

Bien que ce soit une caractéristique qui n'est pas si courante dans d'autres jeux de balle, il est nécessaire de pouvoir influencer de manière significative la vitesse de la balle et la rotation (changement) lors du lancement d'une balle. Je ne sais pas si ce format est correct, mais dans Snowball Game, "En fonction du nom donné à la boule de neige qui devient la balle" Il prend la forme "d'une balle changeante est lancée". Il existe un événement appelé ProjectileLaunchEvent pour détecter que la balle a été lancée. Utilisation. Quand cet événement est appelé, faites-le comme ```event.getEntity (). GetShooter () pour vérifier le contenu de la main principale du joueur qui lance et régler la vitesse de la balle pour qu'elle corresponde à son nom. Vous pouvez le changer. (Si vous pouvez lancer de la main libre, cochez la main si la main principale n'est pas le ballon mais la main gauche est le ballon.)

Pour la sphère changeante, utilisez BukkitRunnable.runTaskTimer (delay, period) ''. Habituellement, vous exécutez avec 0 retard et 1 période. Écrivez de manière à ce que l'entité de la balle lancée elle-même, la direction du changement et la quantité de changement soient reçues et que la vitesse de chaque balle de tick soit modifiée. Cependant, il y a certaines choses à prendre en compte lors de la détermination de la direction du changement de balle. Le changement vertical peut être exprimé par la fluctuation dans la direction Y, mais le changement horizontal dépend de la direction dans laquelle le joueur qui lance fait face. À partir du monticule, la base de départ peut être dans la direction X ou dans la direction Z, et de plus, il peut s'agir d'un stade en diagonale, de sorte que les joueurs peuvent utiliser le ballon à langer confortablement dans ces différentes variations du stade. Je dois le faire. Par conséquent, utilisez Location.getDirection () '' pour la direction du changement horizontal. Tout ce que vous avez à faire est de définir la ligne de visée actuelle du joueur comme avant.

getHorizontalMove


//move représente la quantité de changement dans la direction de prise de vue par tick lorsque le signe est positif, et la quantité de changement dans la direction du curseur lorsque le signe est négatif.
static Vector getHorizontalMove(Player player, double move, boolean fromMainHand){
    //Lors du lancer vers la gauche, inversez le sens du changement latéral
    int modifierWithHand = 1;
    if(player.getMainHand() == MainHand.LEFT && fromMainHand || player.getMainHand() == MainHand.RIGHT && !fromMainHand){
        modifierWithHand = -1;
    }
    Location loc = player.getLocation();
    //Lacet pour la direction vers le côté principal+Orientation à 90 °
    loc.setYaw(loc.getYaw() + 90 * modifierWithHand);
    return loc.getDirection().normalize().multiply(move);
}

En augmentant le lacet de la position du joueur de +90 degrés et en acquérant la direction de cette manière, la direction «droite» du joueur peut être obtenue. Après cela, vous pouvez changer le code de 90 en fonction du type de balle changeante et du bras de lancement. Au fait, Snowball Game vous permet de définir "quel type de nom changera quel type de changement" dans le fichier de configuration.

problème

Il n'y a presque aucun problème avec cette implémentation en ce qui concerne le lancer, mais contrairement à d'autres jeux de baseball, Minecraft peut lancer des balles changeantes dans n'importe quel nombre de directions, de sorte qu'il peut se comporter de manière étrange. Par exemple, lorsqu'une quatre couture (supposée être définie comme une balle qui change vers le haut) est lancée directement au-dessus, le comportement attendu est en arrière vu du lanceur car la quatre couture est à l'origine une balle backspin. Bien qu'il change vers l'arrière, dans cette implémentation, le changement de direction verticale est uniformément défini comme la fluctuation liée à la coordonnée Y, donc il se comportera comme s'il continuait à monter tout droit et ne tomberait pas facilement. ..

Si c'est la seule chose, cela n'aura pas beaucoup d'effet dans le jeu réel, et ce ne sera pas un problème si vous le rejetez avec "parce que c'est un jeu" et "parce que c'est un plug-in tout au plus", mais la mise en œuvre de cette balle changeante est pour frapper la balle. Il y a un problème lors de la tentative de candidature. ** La mouche attrape ne revient pas. ** ** La méthode détaillée sera décrite plus loin, mais tant que nous préconisons un plug-in de baseball, nous voulons naturellement donner à la balle frappée un changement de trajectoire tel que «allongement» ou «tranche» en fonction de la qualité du coup. Pour y parvenir, nous voulons déterminer la direction du changement au moment de l'impact et utiliser le même Bukkit Runnable utilisé pour implémenter la sphère changeante. Ensuite, la mouche qui devrait avoir un backspin changera simplement vers le haut de chaque tick. Cela signifie qu'une mouche receveuse qui devrait avoir un backspin extrêmement fort sera simplement une mouche qui "ne tombe pas facilement". ** Cela ne devrait pas être. La mouche attrape doit faire face au côté arrière du filet et l'attraper. ** ** C'est trop solitaire pour être incapable d'exprimer cette difficulté particulière.

Solution

Une simple connaissance de la dynamique des fluides a été utile comme solution à ce problème. Il semble qu'une force de levage soit générée dans une direction proportionnelle au produit extérieur du vecteur de rotation (expression vectorielle de rotation) du vecteur de mouvement du cylindre / sphère qui se déplace en tournant dans le fluide. C'est ce qu'on appelle l'effet Magnus. J'ai l'impression que je l'ai souvent entendu à l'apogée de Fujikawa Kyuji. Je suis une personne qui n'a même pas fait de physique au lycée, donc même si on dit que c'est le produit externe du vecteur, cela ne me vient toujours pas à l'esprit, mais quand j'ai fait cela de la loi de la main gauche de Fleming avec ma main droite, je suis allé dans la direction de mon index et j'ai tourné mon pouce autour de l'axe. Il semble qu'une balle qui tourne dans le sens des aiguilles d'une montre se penche vers le majeur. Si vous essayez, il changera sûrement vers l'arrière lorsque la balle de backspin se déplace vers le haut, et il changera vers l'avant quand il montera et descendra. De plus, la quantité de changement dépend de la quantité de rotation et de la vitesse de la balle à ce moment. Plus précisément, en supposant que le diamètre de la balle est d'environ 73 mm et que la condition atmosphérique est l'état standard, `` 1/8 * Math.PI * Math.PI * 1.205 * Math.pow (73/1000, 3) * velocity.length () * Il semble s'agir de rps '' (référence). (rps est le nombre de tours par seconde. Notez que vous le voyez souvent dans Trackman car RPM est l'unité.) En d'autres termes

BallMovingTask


import org.bukkit.entity.Projectile;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.Vector;

public class BallMovingTask extends BukkitRunnable {
    private Vector spinVector;
    private Projectile ball;
    private int rps;
	public BallMovingTask(Projectile ball, Vector spinVector, int rps) {
        this.ball = ball;
        this.spinVector = spinVector;
        this.rps = rps;
    }
    @Override
    public void run() {
        //Annulez la tâche si la balle meurt après avoir rebondi ou frappé
    	if(ball.isDead()){
    		this.cancel();
    	}
    	Vector velocity = ball.getVelocity();
    	if(spinVector.length() != 0){
	    	Vector actualMove = velocity.getCrossProduct(spinVector);
	    	if(actualMove.length() != 0){
	    		actualMove.normalize().multiply(1/8 * Math.PI * Math.PI * 1.205 * Math.pow(73/1000, 3) * velocity.length() * rps);
	    	}
	    	velocity.add(actualMove);
    	}
    	ball.setVelocity(velocity);
    }

}

Vous pouvez créer un Runnable comme celui-ci, et lorsque vous le lancez, vous pouvez demander des rps et spinVector et le passer. Cependant, Snowball Game met l'accent sur la caractéristique que "la quantité de changement par tick et la direction du changement peuvent être déterminées par la configuration", et "après avoir trouvé la direction du changement vue du joueur qui lance en fonction de la valeur numérique de la configuration" Le produit extérieur du vecteur lancé de la balle est pris et utilisé comme vecteur de rotation. "" La valeur de la quantité de changement est donnée comme la longueur du vecteur de rotation, et le mouvement réel est ajusté à cette longueur (la formule ci-dessus n'est donc pas utilisée). " Il est devenu. Dans tous les cas, cela ramènera la mouche catcher en place et la mouche coupée sera plus susceptible de caler. 2018-06-01_18.02.31.png En utilisant cette méthode de calcul, c'est également une caractéristique que l'orbite du home run n'est plus une ligne parabolique symétrique.

Coup

Je ne connais pas du tout la situation réelle, mais je pense que c'est la partie frappante qui donne le plus d'individualité à la mise en œuvre dans les matchs de baseball. Si vous souhaitez reproduire exactement le coup, vous devez définir la forme de la batte, puis juger le coup avec la balle volante, mais il est assez difficile de l'implémenter dans Minecraft. Alors, quel genre d'éléments le joueur doit-il ressentir que «le coup peut être reproduit»? Comme une exigence pour composer un coup au baseball

Je pense qu'il y a quelque chose comme ça. Pour répondre à ces exigences, Snowball Game utilise des arcs comme des chauves-souris.

Comme l'arc a à l'origine une jauge de force de traction, il est très facile d'ajuster la force. Spigot a également un EntityShootBowEvent qui peut détecter quand un joueur tire un arc. De plus, cet événement peut obtenir l'entité de la flèche qui était censée être libérée même si ```event.setCancelled (true) `, et cette flèche est considérée comme une chauve-souris car elle est` `ʻEntity.getNearbyEntities (x) En exécutant, y, z) et en déterminant s'il y a ou non une entité balle dans la valeur de retour, il est possible de déterminer la touche entre la balle et le bâton. Jusqu'à ce point, il est possible de juger de la force du swing et du swing / hit. Tout ce que vous avez à faire est de clarifier la question du type de balle que la balle frappe la batte. Pour cela, nous avons décidé d'utiliser `` Location.subtract (Location) '' pour trouver et utiliser le "vecteur de la flèche qui ressemble à une chauve-souris à la position où se trouve la balle". Si la balle est au-dessus de la position où la batte est balancée, ce sera une mouche, et si la balle est à l'extérieur, ce sera un couloir. De plus, si vous êtes en retard et que le ballon est derrière vous, ce sera une faute qui vole vers le filet arrière. Le comportement est presque comme un sentiment.

La force de la balle frappée peut être affaiblie car la balle est plus éloignée de la flèche. Il existe de nombreuses façons de l'ajuster, mais dans Snowball Game, le vecteur de la batte à la balle est normalisé puis `` Math.pow (1.3, -batToBall.length ()) '' est appliqué. Je prends la méthode. Une valeur de 1 ou plus à la puissance de -x devient 1 lorsque x est 0, et à mesure que x augmente, elle diminue continuellement et ne devient pas un nombre négatif, il est donc difficile de gérer des valeurs inattendues .. Sur la base de ce qui précède,

getBattedBall


//GetBattedBall de EntityShootBowEvent(event.getEntity(), event.getProjectile)Appel sous la forme de
static Projectile getBattedBall(Projectile arrow, float force){
    //La plage où la balle et la batte frappent est uniforme pour simplifier 1.2
    Collection <Entity> nearByEntities = arrow.getNearbyEntities(1.2,1.2,1.2);
    for(Entity entity : nearByEntities){
        if(Util.isBall(entity)){
	        Vector batToBall = entity.getLocation().subtract(arrow.getLocation()).toVector();
	        //3 ici.5 est une valeur qui donne un home run avec une bonne sensation. Il n'y a aucune base particulière pour en déduire.
	        double speed = Math.pow(1.3, -batToBall.length()) * force * 3.5;
	        Projectile battedBall = (Projectile)entity.getWorld().spawnEntity(entity.getLocation(), EntityType().Snowball);
	        battedBall.setShooter(arrow.getShooter());
            battedBall.setVelocity(batToBall.normalize().multiply(speed));
		    return battedBall;
		}
	 }
	 return null;
}

Ce sera quelque chose comme ça. Après cela, vous pouvez ajouter des assaisonnements tels que «Plus la vitesse de lancer est rapide, plus il est facile de voler» et «La portée qui frappe la chauve-souris rend Y plus petit».

problème

Le premier problème avec cette méthode peut être immédiatement apparent pour ceux qui ont lu attentivement la section sur les lancers ci-dessus. En d'autres termes, il est difficile de définir la rotation de la balle frappée. Il est possible de simuler l'état de la balle au moment de la collision à partir de la courbure de la batte et de l'élasticité de la balle, mais je ne veux pas faire un calcul aussi difficile. Je veux utiliser ceci parce qu'il y a un besoin pour un élément qui puisse être utilisé pour définir la rotation de la balle frappée, qui est la "relation de position entre la batte et la balle". Ensuite, je voudrais prendre la direction de la balle frappée et le produit extérieur du vecteur de la batte à la balle de la même manière que celle expliquée à la fin du lancer, mais ce n'est pas non plus possible. En effet, les deux vecteurs ont la même orientation mais ne diffèrent que par la longueur. Le produit extérieur des vecteurs parallèles est toujours un vecteur nul. (Identique à la théorie selon laquelle la boule gyroscopique devient un curseur vertical)

Un autre problème est que j'ai fait une vidéo avant, alors jetez un œil. En bref, il n'est pas possible de produire une balle qui a à la fois une vitesse de frappe élevée et un angle de frappe élevé. Si vous frappez la balle plus haut que la position de la flèche, la mouche sera plus élevée, mais la vitesse de frappe diminuera à mesure que la distance augmente, et si vous frappez la flèche plus près de la balle, la vitesse de frappe augmentera, mais la balle frappera à un angle inférieur. Devenir. C'est la même chose pour le goro, et il est difficile de créer un goro qui perce le sol à un angle profond et saute haut. En d'autres termes, la balle frappée est devenue moins diversifiée.

Alors, comment pouvons-nous résoudre ces problèmes?

Implémentation de la trajectoire de swing

Dans SnowballGame, la solution était de définir un vecteur "swing" et de l'ajouter au vecteur de balle frappée lors de la frappe. De cette manière, le vecteur de mouvement de la balle frappée pointe dans une direction différente de la batte au vecteur balle, et le produit extérieur des deux vecteurs peut être pris comme vecteur de rotation de la balle frappée. De plus, la variété des balles de frappe peut être sécurisée en manipulant la trajectoire de swing. Ce serait encore mieux si chaque frappeur pouvait avoir sa propre personnalité, comme le swing supérieur et le swing down.

La question est alors de savoir comment définir cette «trajectoire de swing». Je pense que le point le plus similaire de ce plug-in est ici, mais mon expérience et livres techniques que j'ai lus dans le passé 83% 90% E3% 83% 83% E3% 83% 86% E3% 82% A3% E3% 83% B3% E3% 82% B0% E3% 81% AE% E6% AD% A3% E4% BD% De 93-% E6% 89% 8B% E5% A1% 9A-% E4% B8% 80% E5% BF% 97 / dp / 4583045654), on dit que le swing au baseball comprend les trois exercices suivants. J'ai pensé.

--Mouvement rotatif parallèle au sol dû à la rotation de la taille

Cette classification n'est pas toujours correcte, mais Snowball Game implémente des trajectoires de swing basées sur elle.

De ces trois, le second est le plus difficile. Ce mouvement est la partie qui fait l'objet de mots qui décrivent un swing, tels que «niveau supérieur vers le bas», et la largeur de swing et la fluctuation du mouvement vertical sont également différentes pour chaque frappeur. En d'autres termes, il n'y a pas d'orbite où "c'est la bonne réponse". Cependant, il y a une trajectoire que l'on peut qualifier d '«idéale» si l'on pousse par un raisonnement forcé. C'est la [courbe de descente la plus rapide](https://ja.wikipedia.org/wiki/%E6%9C%80%E9%80%9F%E9%99%8D%E4%B8%8B%E6%9B%B2 % E7% B7% 9A). En bref, cela semble être une orbite qui "atteint le plus rapidement lorsque seule la gravité agit et se déplace d'un certain point sur le plan à un certain point". En d'autres termes, si nous supposons que "le temps nécessaire pour se balancer doit être aussi court que possible", alors c'est la trajectoire de swing idéale. En fait, le mouvement de balancement de la chauve-souris n'est pas «un mouvement qui n'agit que par la gravité», il est donc toujours suspect, mais au moins il ne devrait pas poser de problème de l'utiliser comme guide pour l'utiliser dans le jeu. (L'autre jour, j'ai entendu dire qu'une fonction spéciale "La trajectoire de swing de Shohei Otani est un cycloïde" a été mise en place dans un certain programme. Cette courbe de descente la plus rapide semble être une cycloïde, donc j'ai pensé qu'elle était réaliste dans une certaine mesure. Cela semble aller.)

Il semble que la courbe de descente la plus rapide sur le plan bidimensionnel soit x = a (θ-sinθ), y = a (1-cosθ) '', donc cette formule de x est utilisée comme coordonnées de X et Z. , La formule de Y doit être appliquée aux coordonnées de Y. Le problème est la direction dans laquelle cette courbe est étirée, mais elle est considérée en combinaison avec le troisième mouvement ci-dessus, et elle est étirée en diagonale vers l'arrière (dans le cas d'un frappeur droitier, la direction pivote de 90 degrés vers la droite de la ligne de visée). Et. Alors

getBatPosition


//Le degré de rotation latérale à mettre en œuvre à l'avenir est défini comme un roulis.
//rollDirection est 1 pour les frappeurs droitiers,Si vous êtes un frappeur gaucher-Cela devient 1.
public static Location getBatPosition(Location eye, double roll , int rollDirection){
	Location eyeLoc = eye.clone();
	eyeLoc.setYaw(eyeLoc.getYaw() - (float)(90 * rollDirection));
	Vector push = eyeLoc.getDirection().setY(0).normalize();
	double theta = Math.abs(roll * 2);
	double x = push.normalize().getX() * (theta - Math.sin(theta));
	double y = -(1 - Math.cos(theta));
	double z = push.normalize().getZ() * (theta - Math.sin(theta));
    return eye.clone().add(x,y,z);
}

Peut être écrit. En utilisant cela, donnez une valeur numérique de 0 à π pour rouler, et exécutez une boucle de particules génératrices à la position obtenue 20 fois, cela ressemblera à ceci. 2018-06-01_17.01.18.png

À ce stade, le deuxième mouvement, la rotation horizontale, doit être mis en œuvre. Pour dire la vérité, jusqu'à ce que je commence à écrire ici, j'ai utilisé Coordinate Transformation pour implémenter cette rotation. En organisant ce que j'écrivais et faisais, j'ai réalisé que ** "j'ai juste besoin de changer Yaw" **. Quand je l'ai réalisé, c'était naturel, mais pourquoi ne l'ai-je pas compris jusqu'à présent ...

getBatPosition


public static Location getBatPosition(Location eye, double roll , int rollDirection){
	Location eyeLoc = eye.clone();
    //↓ Je devais juste changer ici ...
	eyeLoc.setYaw(eyeLoc.getYaw() - (float)(90 * rollDirection) - Math.toDegrees(roll));
	Vector push = eyeLoc.getDirection().setY(0).normalize();
	double theta = Math.abs(roll * 2);
	double x = push.normalize().getX() * (theta - Math.sin(theta));
	double y = -(1 - Math.cos(theta));
	double z = push.normalize().getZ() * (theta - Math.sin(theta));
    return eye.clone().add(x,y,z);
}

Donc, c'est ce que j'ai essayé de produire des particules de la même manière qu'avant. 2018-06-01_18.12.51.png Étant donné que le balancement part de la direction décalée de 90 degrés vers la droite, la direction dans laquelle la ligne de visée fait face, c'est-à-dire la direction qui devient le point de rencontre, est celle où le roulis devient π / 2. Puisque thêta est deux fois la valeur absolue de roll, Y prend la valeur la plus basse à ce moment.

En regardant ces particules, il peut sembler que la position de balancement est trop éloignée du corps, mais cette trajectoire de balancement n'est pas utilisée pour la détermination de la frappe, il n'y a donc pas de problème. Seule la relation entre la position de la flèche et la balle est utilisée pour la détermination de la frappe, et cette trajectoire de swing est utilisée uniquement pour calculer la force appliquée de la batte à la balle lorsque la batte frappe la balle.

Cette partie est

//Rencontrez le ballon à une ligne de vue et à 0 à partir de là.01 On suppose que la balle et la batte étaient en contact l'une avec l'autre jusqu'à la position tournée par Radian (ce nombre n'est pas fondé).
Vector batMove = getBatPosition(player.getEyeLocation(), (Math.PI / 2 + 0.01) * rolld, rolld,).subtract(getBatPosition(player.getEyeLocation(), Math.PI / 2 * rolld, rolld,));

Ce serait bien de se sentir comme ça.

Sous-produit

En exprimant la trajectoire du swing de cette manière, le problème de "comment définir le swing supérieur et le downswing" peut être résolu simplement. Dans le `` getBatPosition () '' ci-dessus, il y a des variables qui représentent deux angles, roll et theta. roll est la rotation horizontale à partir de la position de départ du swing, et theta est le degré de rotation du cercle utilisé pour dessiner la courbe de descente la plus rapide. Comme expliqué précédemment, si thêta est égal à deux fois le roulis, le point auquel la balle est rencontrée est le point le plus bas de la courbe de descente la plus rapide. Avant ce point, l'orbite est de haut en bas, et si elle est derrière ce point, l'orbite est de bas en haut. En d'autres termes, cela pourrait être défini comme un "changement de niveau".

Cela signifie que s'il y a un point d'impact de la balle avant ce point, il peut être exprimé comme un downswing, et s'il est plus tard, il peut être exprimé comme un swing supérieur. En d'autres termes, on peut dire que si une certaine valeur est ajoutée à thêta, la valeur ajoutée devient le swing supérieur, et si elle est réduite, le montant réduit devient le downswing. Cela signifie


public static Location getBatPosition(Location eye, double roll , int rollDirection, double upper){
    Location eyeLoc = eye.clone();
    eyeLoc.setYaw(eyeLoc.getYaw() - (float)(90 * rollDirection - Math.toDegrees(roll)));
    Vector push = eyeLoc.getDirection().setY(0).normalize();
    //S'il dépasse 1, le cycloïde fera le tour.
    if(Math.abs(upper) > 1){
        upper = 1 * Math.signum(upper);
    }
    //Je veux définir upper sur une valeur qui peut être définie dans la configuration, je l'ai donc définie sur un grossissement de π pour une compréhension facile.
    double theta = Math.abs(roll * 2) + Math.PI * upper;
    double x = push.normalize().getX() * (theta - Math.sin(theta));
    double y = -(1 - Math.cos(theta));
    double z = push.normalize().getZ() * (theta - Math.sin(theta));
    return eye.clone().add(x,y,z);
}

Si vous l'écrivez ainsi, plus la valeur supérieure est élevée, plus il est facile de frapper la mouche, et plus elle est petite (le signe est négatif), plus il est facile de frapper le goro. En faisant cela, le joueur pourra définir dans la configuration "quel nom de batte doit être utilisé pour quel type de swing" dans le même mécanisme qu'une balle changeante. On peut dire que la personnalisation a été améliorée.

De plus, la force du bâton à la balle calculée dans cette condition est dans le sens de "tirer" pour le frappeur. Par conséquent, lorsque cela est ajouté à la balle, la balle frappée dans la direction «tirante» est plus susceptible d'avoir une longue distance de vol. On peut dire qu'un coup plus réaliste est devenu possible.

A continué

Dans le prochain article, j'écrirai sur la partie attrapante et des astuces qui peuvent être utilisées pour mettre en œuvre d'autres techniques de balle.

Recommended Posts

Non-physique similaire utilisé pour exprimer le baseball sur Minecraft et mise en œuvre dans Spigot (lancer / frapper)
Non-physique similaire utilisé pour exprimer le baseball sur Minecraft et mise en œuvre dans Spigot (édition reliée)
Non-physique similaire utilisé pour exprimer le baseball sur Minecraft et mise en œuvre dans Spigot (capture de balle / Conseils)