touchesMoved appelé à intervalles irréguliers

Je suis en train de créer un jeu pour iOS où vous faites glisser de gros objects sur l'écran. Lorsque je cours le jeu sur un iPad / iPhone réel pendant un certain time (en faisant glisser l'object en cercles sur l'écran) toutes les 5 minutes, l' object traîné va tout stuttery pendant environ 10-30 secondes . Ensuite, cela revient à bouger soyeux.

Visuellement, il semble que le taux de rafraîchissement du jeu soit tombé à 15 fps pendant un moment, mais en réalité il tourne à 60 fps à tout moment. Cependant, j'ai remarqué que la seule chose qui ne bouge pas facilement est l'object traîné , tandis que le rest du jeu fonctionne parfaitement.

Cela m'a amené à croire que le bégaiement est lié à l'input tactile dans iOS. J'ai donc commencé à regarder les touches, et j'ai vu qu'il est normalement appelé toutes les 16 millisecondes (donc l'input tactile tourne à 60 fps). Jusqu'ici tout va bien.

Puis j'ai remarqué que lorsque l'object commence à bégayer, touchesMoved commence à être appelé à des intervalles de time bizarres, fluctuant de façon extravagante entre 8 millisecondes et 50 millisecondes.

Ainsi, alors que l'écran tactile est dans cet état étrange, parfois touchesMoved sera appelé seulement 8 millisecondes après l'appel précédent, et parfois il ne sera pas appelé jusqu'à 50 ms après l'appel précédent. Bien sûr, cela rend l'object traîné tout agité parce que sa position est mise à jour à intervalles irréguliers.

Avez-vous une idée de ce qui pourrait être à l'origine de touchesMoved pour cesser d'être appelé à intervalles réguliers, comme il le fait normalement?


Prime:

-Quand j'incline l'écran pour forcer l'orientation de l'écran à changer, environ 70% du time l'écran tactile va dans l'état mentionné ci-dessus où touchesMoved commence à être appelé irrégulièrement. Puis, après 10-20 secondes, il redevient normal et tout redevient lisse.

-J'ai essayé ceci sur deux iPads et deux iPhones, avec iOS 6 et 7, et le problème apparaît dans tous ces appareils.

-Une vue OpenGLES est utilisée pour afficher les charts. Il se synchronise sur la fréquence de rafraîchissement de l'affichage à l'aide de CADisplayLink .

-Le projet Xcode que j'utilise pour tester cela a été généré par l'outil de développement de jeu unity3d, mais j'ai trouvé plusieurs jeux non-unitaires où le même problème apparaît. Cela semble être un problème à l'échelle du système. note Je mesure les timings dans l'objective-c en utilisant CFAbsoluteTimeGetCurrent , complètement en dehors de l'unité.

Ce n'est pas un bug dans Unity.

Quelque chose à l'intérieur du operating system est dans un mauvais état et les messages tactiles s'arrêtent de couler doucement. Parfois, vous aurez plusieurs mises à jour dans un cadre et parfois vous n'en aurez pas.

Le problème ne se produit pas sur iPhone4 ou ci-dessous, ou si le jeu fonctionne à une fréquence d'images de 30Hz.

J'ai rencontré ce bug en utilisant un moteur interne que j'avais écrit en travaillant dans une entreprise précédente. Il se manifeste d'abord après la mise à niveau du système d'interface user d'un jeu de genre Scrabble, où vous faites glisser les tuiles sur l'écran. C'était tout à fait bizarre, et je n'ai jamais été capable de déterminer exactement les étapes de la reproduction, mais elles semblent liées au timing, d'une manière ou d'une autre.

Il peut également être vu sur Angry Birds (à less qu'ils ne l'aient réparé maintenant), et une variété d'autres jeux sur le marché, fondamentalement n'importe quoi avec le défilement ou le mouvement de contact-glisser. Pour Angry Birds, il suffit d'aller dans un niveau et commencer à défiler sur le côté. La plupart du time, ce sera soyeux, mais peut-être 1 à 10 fois, ça va être maladroit. Redémarrez l'application et réessayez.

Une solution de contournement consiste à supprimer la fréquence de mise à jour d'input à 30Hz pour quelques images. Cela secoue d'une manière ou d'une autre les composants internes de l'OS et lui donne le time de se redresser, donc quand vous le remettez en marche jusqu'à 60Hz, il fonctionne de nouveau en douceur.

Faites cela chaque fois que vous détectez que le mauvais état a été entré.

J'ai aussi rencontré ça. Je peux vérifier que c'est un bug dans Unity 4.3.x. Mon process de build 4.2.x touche le périphérique à 60Hz avec TouchPhase.Moved sur chaque image. Mes builds 4.3.x obtiennent 20-40Hz avec TouchPhase.Stationary étant émis sur des images perdues.

L'histoire de TestFlight a sauvé ma santé mentale ici.

Ne pas oublier de déposer un bug avec UT.

C'est un vrai désastre. Parfois c'est le décalage et parfois c'est vraiment lisse. Il est en retard même lorsque l'utilisation du processeur graphique et du processeur est inférieure à 10%. Ce n'est pas un bug d'Unity. J'utilise cocos2d v3. Si quelqu'un a trouvé un remède, postz le!

J'ai couru dans cela aussi. Mon hypothèse actuelle que je dois encore vérifier est que si vous requestz une fréquence d'images donnée (par exemple 60fps), mais que votre fréquence d'image réellement atteinte est inférieure à celle (par exemple 45fps) cela provoque un problème de synchronisation Unity demandant des inputs d'iOS à une fréquence d'images plus élevée que celle en cours d'exécution. Cependant, si vous le réglez à 30fps (au less dans mes tests UNity 5.2 avec iOS 9.1), vous obtenez une input solide de 30Hz. Quand j'ai désactivé un morceau de mon jeu et qu'il fonctionnait à 60fps très solide, je recevais systématiquement 60Hz d'input depuis l'écran tactile. C'est ce que j'ai pour l'instant, mais je dois le prouver dans un projet simple que je n'ai pas encore eu le time de faire. J'espère que ceci aide quelqu'un d'autre.

J'ai trouvé une solution potentielle à ce problème ici: https://forums.developer.apple.com/thread/62315 (postr ici parce qu'il m'a fallu beaucoup de searchs pour trébucher sur ce lien alors que cette question StackOverflow était le premier résultat de Google ).

Pour faire suite à cela, j'ai eu une réponse sur mon rapport de bug à Apple. C'est la réponse:

"Tant que vous n'effectuez aucune mise à jour de l'écran, l'écran rest en mode basse consommation et donc en mode 30hz, ce qui maintient le stream d'input de l'événement à 30 Hz. Une solution consiste à provoquer une mise à jour , même s'il ne s'agit que d'un mouvement d'un pixel d'une vue si une input est nécessaire alors qu'aucune mise à jour d'écran explicite ne sera déclenchée. "

Dans mon application, en utilisant un GLKView, j'ai mis son preferredFramesPerSecond à 60. De time en time, mon taux d'input tomberait au hasard à 30hz. La réponse d'Apple n'explique pas complètement pourquoi cela se produirait, mais apparemment la méthode attendue pour gérer ceci est d'appeler display () directement à partir de touchesMoved () en faisant glisser.

J'ai sous-classé GLKView et j'ai mis preferredFramesPerSecond à 60. Sur touchesBegan (), je mets isPaused = true, et commençons à appeler display () dans touchesMoved (). Sur touchesEnd (), j'ai mis isPaused = false. En faisant cela, je n'ai plus aucun problème – l'application est plus performante que jamais.

L'exemple d'Apple TouchCanvas.xcodeproj dessine tout à partir de touchesMoved (), donc je suppose que c'est la façon de gérer cela.

Pour autant que je sache, votre meilleur pari pour get un look lisse peut être d'interpoler entre les events tactiles, plutôt que de cartographier immédiatement vos objects à votre position tactile.

tl; dr: Il semble que le simple fait d'avoir un CADisplayLink provoquant un context OpenGL ou un dispositif Metal à dessiner à 60fps peut causer cela.


Je repro'ing ceci sur mon iPhone 7 Plus w / iOS 10.2.1.

J'ai fait un petit exemple d'application simple avec un CADisplayLink avec preferredFramesPerSecond = 60 , et j'ai essayé les approches de rendu suivantes:

  1. GLKView avec display()
  2. CAEAGLLayer , utilisé tel que prescrit par Apple à la WWDC (opaque, couche unique, plein écran, rien ne se dessine dessus)
  3. MTLDevice

Dans chaque cas, la méthode de rendu ne ferait que vider l'écran, ne pas essayer de dessiner autre chose.

Dans chaque cas, j'ai vu le problème du taux d'input.


Les "trucs" suivants semblaient aussi ne rien faire, quand ils touchesMoved appelés à l'intérieur des touchesMoved :

  1. Appel de glkView.setNeedsDisplay() (avec enableSetNeedsDisplay défini sur true )
  2. Déplacer une autre vue
  3. Appeler glkView.display() (en fait, il semble que cela peut augmenter votre taux d'input à 40 events par seconde.) Mais pour autant que je glkView.display() cela ne semble pas mieux, et je ne le ferais pas. le reorder.)

J'ai abandonné, après avoir exécuté tous ces tests. Au lieu de cela, j'interpole mon object entre les positions de contact à la place. Donc c'est ce que je reorderais aussi.

Mais j'ai pensé que j'inclurais mes searchs ici au cas où quelqu'un d'autre prendrait le coup et findait une meilleure solution.