Comment mettre en place une poutre de tracteur?

8

Je travaille sur un jeu où le joueur peut ramasser des objets en utilisant quelque chose comme un rayon tracteur et les transporter.

Attirer l'objet vers le centre du faisceau n'est pas difficile. Mais une fois que l'objet est suffisamment proche du centre, je dois le garder là pendant que le joueur bouge, ce qui me pose problème. Je peux penser à deux façons de le faire, et les deux ont des problèmes:

  1. Mettez à jour la position de l'objet chaque fois que la position du joueur change, en le gardant centré sur la poutre.

  2. Mettez à jour la vitesse de l'objet pour pointer directement vers le centre du faisceau, plus loin, plus la vitesse est élevée.

Le déplacement et la rotation fonctionnent très bien avec les deux approches, mais la physique est erronée lorsque l'objet transporté entre en collision avec d'autres objets:

Avec la première approche, la physique est complètement ignorée. L'objet transporté ne fait qu'éloigner quoi que ce soit. En effet, les changements de position ne sont censés être effectués que dans le cadre de la physique mondiale, en fonction de la vitesse.

Avec la deuxième approche, la physique se comporte essentiellement comme elle le devrait, mais réagit de manière excessive. Le problème est le suivant: afin de maintenir l'objet transporté au centre du faisceau même en rotation et en mouvement, je dois utiliser des valeurs de vitesse élevées. Ainsi, une fois que l'objet transporté en a touché un autre, la vitesse de la collision est beaucoup trop élevée.

Comment puis-je l'implémenter correctement? Ma meilleure supposition en ce moment est d'aller avec la deuxième approche et d'ajouter une manipulation spéciale pour les objets transportés à la physique du monde, en réduisant la vitesse à des valeurs sensées pour les collisions ou lorsque le joueur cesse de les transporter. Mais cela semble être une solution de contournement assez inélégante.

Edit: Ajout d'un pseudo code pour illustrer comment cela fonctionne en ce moment (ce serait la deuxième approche ci-dessus)

void attract_object(object, ticks) {
    Vector distance = beam_center - object.center;
    // If the object is not close to the beam center, attract it slowly
    if (magnitude(distance) > 10) {
        object.velocity += distance.normalized() * ticks * 0.1;
        return;
    }

    // Here comes the part we're talking about. That magic 0.5 is just high enough
    // that the object isn't lost while moving around. But it's still so high that
    // other objects are repelled with way too much force.
    object.velocity = distance * ticks * 0.5;
}

D'après ce que je vois, cela se produit lorsque l'objet transporté repousse un autre objet:

  1. L'objet transporté entre en collision avec un autre objet
  2. Les vitesses des objets sont distribuées correctement, donc l'objet transporté est éloigné du centre du faisceau dans le processus
  3. Le code ci-dessus provoque le retour de l'objet transporté au centre du faisceau, avec une telle vitesse qu'il y retournera rapidement
  4. Lorsque l'objet transporté recule vers le centre du faisceau, la moitié de sa vitesse élevée est transférée à l'autre objet, le repoussant violemment. Étant donné que la vitesse initiale de l'objet transporté semble être saine, je peux imaginer que les étapes 2 à 4 sont répétées plusieurs fois, créant une vitesse aussi élevée.

Cela semble être la cause. Je ne peux pas penser à une belle façon de le réparer :(

futlib
la source
1
Bienvenue dans la complexité d'un réacteur à fusion Tokomak. Vous avez l'avantage de n'avoir besoin que de construire un modèle mathématique fonctionnel, pas la bouteille magnétique fonctionnelle, mais les mathématiques sont identiques et non triviales. Ce que vous essayez est faisable, mais vous devrez réfléchir attentivement à votre modèle mathématique avant de coder.
Pieter Geerkens

Réponses:

1

Essentiellement, ce que vous recherchez, c'est que l'objet «rayonné» se comporte exactement comme si vous le saisissiez avec vos mains.
Une option serait de lui faire partager les vitesses d'accélération a / o de la «main» qui le tient au lieu d'ajuster sa vitesse pour combler l'écart avec le centre du faisceau.

Disons que le centre du faisceau est la main qui tient. si votre personnage pivote de 90 degrés vers sa gauche en 1 seconde, alors la vitesse de la main serait:

If R = length of the arm: which is the radius of the rotation circle
R^2 *PI /4 would be the distance traveled over a second.
faites-le fois le temps écoulé du cadre pour trouver la vitesse que vous devez appliquer à votre objet. Trouvez la normale horizontale au faisceau pour trouver son vecteur de direction.

Mon point est que vous n'avez pas à résoudre les problèmes si vous essayez d'autres implémentations qui ne le causeront pas en premier lieu.

J'irais jouer avec le pistolet à gravité dans HL2 pour trouver de l'inspiration sur le problème, mais j'ai d'autres plans pour aujourd'hui.

EDIT: je suis désolé, je pensais que c'était pour un pistolet 3D, mais c'est essentiellement la même chose avec 2D sauf que les axes sont différents (et il n'y a pas de géométrie complexe)

icosamuel
la source
Après avoir essayé différents hacks, c'est ce qui m'a mis sur la bonne voie, le résultat semble bon. La vitesse de collision est encore un peu trop élevée, mais je pense que je peux le comprendre. Peut-être simplement en n'attirant aucun objet à grande vitesse dans une direction différente.
futlib
Peut-être que lorsque la «main» entre en collision avec un mur, vous pouvez calculer la position plausible la plus proche (qui ne se heurte pas) de la main et l'utiliser comme «main temporaire» pendant la collision. J'aime souvent aborder les problèmes sous un autre angle. Je suis content d'avoir pu aider avec ça;).
icosamuel
4

Que diriez-vous d'ajouter une connexion à ressort, c'est-à-dire de forcer l'objet transporté à revenir à la position de transport en fonction de la distance, tout en lui permettant d'être repoussé par des objets solides (comme des murs).

Ajustez constamment la vitesse de l'objet transporté (en changeant l'accélération en fonction de la position / distance) pour pointer vers la position du faisceau tracteur (c'est-à-dire votre deuxième approche). Si l'objet est trop éloigné, supprimez la connexion (et l'objet).

Je ne sais pas vraiment pourquoi vous auriez besoin de vitesses élevées. Surtout le cas "player let go" indiquerait que votre vitesse de rotation pourrait être trop élevée ou irréaliste. N'oubliez pas non plus des éléments tels que la résistance à l'air et la gravité.


Edit: Compte tenu du code mis à jour, le problème est plutôt trivial à trouver:

if (magnitude(distance) > 10) {
    object.velocity += distance.normalized() * ticks * 0.1;
    return;
}

Le problème ici est le cas où la distance de l'objet à sa position de but est constamment trop éloignée (ie > 10). Tant que cette condition est vraie, sa vitesse augmente simplement encore et encore (c'est-à-dire indéfiniment).

Deux solutions possibles pour cela:

Définissez une vitesse maximale:

object.velocity = min(object.velocity + distance.normalized() * ticks * 0.1, max_velocity);

Appliquez une vitesse fixe plutôt que d'accélérer:

object.velocity = distance.normalized() * ticks * magic_factor;

Accélérer tout en étant trop loin est certainement une mauvaise approche ici. Une chose à propos de la traction d'un ressort ou d'un élastique: peu importe que vous le teniez pendant une seconde ou une minute. À la fin, il accélérera de la même manière (étant donné qu'il n'a pas été en mouvement et qu'aucune autre force n'est appliquée).

Mario
la source
C'est ce que j'ai décrit dans la méthode 2, n'est-ce pas? C'est la physique de base du printemps AFAIK. Je vais ajouter du code à la question ci-dessus pour l'illustrer.
futlib
Suppression de la partie concernant la vitesse incorrecte ci-dessus, elle est très bien, viens de la tester. Ce ne sont donc que les collisions avec d'autres objets qui sont gâchées.
futlib
Oui, c'est essentiellement votre deuxième approche. Mise à jour de ma réponse.
Mario
J'ai essayé les deux approches, mais cela n'aide pas. Il semble que la magnitude (distance)> 10 cas n'est pas le coupable ici. J'ai essayé de limiter la vélocité pour le cas <=, mais c'est le problème habituel: la vélocité est soit si basse que l'objet est lâché, soit si haute qu'elle repousse violemment les autres.
futlib
"si haut qu'il repousse violemment les autres": vous devez déplacer d'autres objets en fonction de la vitesse effective, pas de la vitesse dans les coulisses (c.-à-d. réinitialiser la vitesse due à la collision).
Mario