Étiquette : how to

  • Unity3D.tips[2]: Intersection point between two lines in 2D

    The code

    The maths behind

    To get the point where two lines intersect, we will do some maths. To the mathematicians who will come across this post, I’m truly sorry for the heart attacks or strokes you may experience by reading this post.

    Ok, let’s take a look on the figure:

    Study case. We can see two lines crossing in an 2D Euclidian space.
    Figure 1

    First, sorry, I was too lazy to make a proper figure using computer tools so I just put a scan. XD

    Next, here are the definitions:

    • AA & BB: the two lines,
    • A1A_1, B1B_1: the arbitrary starting points of the two lines,
    • A2A_2, B2B_2: the arbitrary points which tells the direction of the two lines,
    • XX: the intersection point,
    • OO: the origin point.

    Kewl. Now, what we want is the intersection point XX between those two lines. In other words, we want to know the position which starts from either lines’ arbitrary starting point, added by the direction of the line multiplied by a scalar value. So in our figure 1, the XX position is:

    • the A1A_1 position added by the components of A1A2\overrightarrow{A_1A_2} multiplied by an unknown which I named λ\lambda
    • the B1B_1 position added by the components of B1B2\overrightarrow{B_1B_2} multiplied by an unknown which I named μ\mu

    In this case, it is clear that λ=μ=0.5\lambda = \mu = 0.5 so it will be easy to check if our final formula is correct. 🙂

    At the point of intersection, we know that:

    OX=OA1+λ×A1A2andOX=OB1+μ×B1B2\begin{array}{rcl} \overrightarrow{OX} = \overrightarrow{OA_1} + \lambda\times\overrightarrow{A_1A_2} & and & \overrightarrow{OX} = \overrightarrow{OB_1} + \mu\times\overrightarrow{B_1B_2} \end{array}

    … which gives us:

    OA1+λ×A1A2=OB1+μ×B1B2OA1OB1=μ×B1B2λ×A1A2\begin{array}{rcl} \overrightarrow{OA_1} + \lambda\times\overrightarrow{A_1A_2} & = & \overrightarrow{OB_1} + \mu\times\overrightarrow{B_1B_2} \\ \overrightarrow{OA_1} – \overrightarrow{OB_1} & = & \mu\times\overrightarrow{B_1B_2} – \lambda\times\overrightarrow{A_1A_2} \end{array}

    But we can’t use this statement exactly like this to solve our equation as we cannot multiply and divide vectors in this raw format. Also we need to reduce the unkowns count to 1. So we will separate our equation into two equations with the magic of matrices (which I don’t understand well at the moment), one for the x component and one for the y:

    {OA1xOB1x=μ×B1B2xλ×A1A2xOA1yOB1y=μ×B1B2yλ×A1A2y\left\{\begin{array}{c}\overrightarrow{OA_1}_x – \overrightarrow{OB_1}_x = \mu\times\overrightarrow{B_1B_2}_x – \lambda\times\overrightarrow{A_1A_2}_x \\ \overrightarrow{OA_1}_y – \overrightarrow{OB_1}_y = \mu\times\overrightarrow{B_1B_2}_y – \lambda\times\overrightarrow{A_1A_2}_y \end{array}\right.

    To make our equation more readable, we will use some shorthands:

    Aα=A1A2,Bα=B1B2,C=OA1OB1\begin{array}{r}A_\alpha = \overrightarrow{A_1A_2}, & B_\alpha = \overrightarrow{B_1B_2}, & C = \overrightarrow{OA_1} – \overrightarrow{OB_1}\end{array}

    … so:

    {Cx=μBαxλAαxCy=μBαyλAαy\left\{\begin{array}{c} C_x = \mu B_{\alpha x} – \lambda A_{\alpha x} \\ C_y = \mu B_{\alpha y} – \lambda A_{\alpha y} \end{array}\right.

    From this point, we can reduce the unknown count. In my case, I have chosen to keep μ\mu instead of λ\lambda:

    {CxAαy=μBαxAαyλAαxAαyCyAαx=μBαyAαxλAαyAαx\left\{\begin{array}{c} C_x A_{\alpha y} = \mu B_{\alpha x} A_{\alpha y} – \lambda A_{\alpha x} A_{\alpha y} \\ C_y A_{\alpha x} = \mu B_{\alpha y} A_{\alpha x} – \lambda A_{\alpha y} A_{\alpha x} \end{array}\right.
    CxAαyCyAαx=μBαxAαyλAαxAαy(μBαyAαxλAαyAαx)CxAαyCyAαx=μBαxAαyλAαxAαyμBαyAαx+λAαyAαxCxAαyCyAαx=μBαxAαyμBαyAαxCxAαyCyAαx=μ(BαxAαyBαyAαx)μ=CxAαyCyAαxBαxAαyBαyAαx\begin{array}{rcl} C_x A_{\alpha y} – C_y A_{\alpha x} & = & \mu B_{\alpha x} A_{\alpha y} – \lambda A_{\alpha x} A_{\alpha y} – (\mu B_{\alpha y} A_{\alpha x} – \lambda A_{\alpha y} A_{\alpha x}) \\ C_x A_{\alpha y} – C_y A_{\alpha x} & = & \mu B_{\alpha x} A_{\alpha y} – \lambda A_{\alpha x} A_{\alpha y} – \mu B_{\alpha y} A_{\alpha x} + \lambda A_{\alpha y} A_{\alpha x} \\ C_x A_{\alpha y} – C_y A_{\alpha x} & = & \mu B_{\alpha x} A_{\alpha y} – \mu B_{\alpha y} A_{\alpha x} \\ C_x A_{\alpha y} – C_y A_{\alpha x} & = & \mu (B_{\alpha x} A_{\alpha y} – B_{\alpha y} A_{\alpha x}) \\ \mu & = & \frac{C_x A_{\alpha y} – C_y A_{\alpha x}}{B_{\alpha x} A_{\alpha y} – B_{\alpha y} A_{\alpha x}} \end{array}

    And finally, you have to check if BαxAαyBαyAαx=0B_{\alpha x} A_{\alpha y} – B_{\alpha y} A_{\alpha x} = 0. This will happen if your two lines are parallel or if there is one line defined as a point such as A1=A2A_1 = A_2 or B1=B2B_1 = B_2; no solution exists in those cases.

    Test 1

    Kewl! Now let’s try it with the Figure 1:

    μ=(11)×1(24)×22×1(3)×2μ=48=0.5\begin{array}{rcl} \mu & = & \frac{(1 – 1) \times 1 – (2 – 4) \times 2}{2 \times 1 – (-3) \times 2} \\ \mu & = & \frac{4}{8} = 0.5 \end{array}

    We can get the XX position:

    {OXx=OB1x+Bαx×μ=1+2×0.5=2OXy=OB1y+Bαy×μ=4+(3)×0.5=2.5\left\{\begin{array}{l} \overrightarrow{OX}_x = \overrightarrow{OB_1}_x + B_{\alpha x} \times \mu = 1 + 2 \times 0.5 = 2 \\ \overrightarrow{OX}_y = \overrightarrow{OB_1}_y + B_{\alpha y} \times \mu = 4 + (-3) \times 0.5 = 2.5 \end{array}\right.

    Test 2

    Ok done! Now let’s try with another figure:

    Study case 2. We can see two lines crossing in an 2D Euclidian space.
    Figure 2
    μ=((2)2)×(1)((2)(3))×0(1.5)×(1)1.5×0μ=41.52.666\begin{array}{rcl} \mu & = & \frac{((-2) – 2) \times (-1) – ((-2) – (-3)) \times 0}{(-1.5) \times (-1) – 1.5 \times 0} \\ \mu & = & \frac{4}{1.5} \approx 2.666 \end{array}

    We can get the XX position:

    {OXx=OB1x+Bαx×μ=2+(1.5)×2.666=2OXy=OB1y+Bαy×μ=(3)+1.5×2.666=1\left\{\begin{array}{l} \overrightarrow{OX}_x = \overrightarrow{OB_1}_x + B_{\alpha x} \times \mu = 2 + (-1.5) \times 2.666 = -2 \\ \overrightarrow{OX}_y = \overrightarrow{OB_1}_y + B_{\alpha y} \times \mu = (-3) + 1.5 \times 2.666 = 1 \end{array}\right.

  • [Unity] Force text re-serialization crash : La solution

    Capture d'écran de la fenêtre de crash
    Plantage de l’éditeur Unity sur la re-sérialisation en texte

    Lorsque vous avez un gros projet Unity entre vos mains et que vous décidez de forcer la sérialisation en texte (Project Settings > Editor > Asset Serialization > Mode: Force Text), il se peut que vous rencontriez un problème de mémoire insuffisante qui fait crasher Unity. Lors du processus de « re-serialisation », Unity charge tous vos assets dans la RAM pour recréer les structures de données du format binaire vers le format texte. Puis arrive le moment tant redouté : l’éditeur n’arrive plus à allouer de la mémoire et crashe !

    Ceci est dû pour l’une des deux raisons suivantes :

    1. l’incapacité d’Unity à allouer la mémoire nécessaire due à la limitation des applications 32-bit.
    2. si vous êtes sur Unity 5, bien que l’éditeur soit 64-bit, il n’y a pas assez de mémoire sur votre PC.

    Quel que soit le cas, pas de panique ! Vos données n’ont pas été corrompues par le crash vu que le travail se faisait uniquement en RAM. Néanmoins, il se peut que vos derniers changements apportés au projet se soient évanouis dans la nature (parfois, il faut fermer l’éditeur proprement pour s’assurer que des changements sont sauvegardés sur le disque).

    Comment remédier à ce problème ?

    Pour ma part, la recherche de solution et la résolution m’ont pris un peu plus d’une heure. J’ai passé pas mal de temps sur les forums sans trouver de réelle réponse précise qui n’implique pas le devoir de redéfinir des valeurs et des références qui ont disparu lors de la conversion. Mais de mon côté, j’ai pu trouver une méthode qui permet de faire la conversion sans perte de valeurs et de références !

    Je suis trop fort !

    Voici donc mon pas à pas :

    1. Déjà, partez d’un projet stable, sans erreur. Corrigez vos bugs avant de convertir les assets en texte.
    2. Archivez votre projet quelque part. Ben oui, c’est important d’archiver avant de faire de opérations dangereuses.
    3. Dans l’éditeur, dans la fenêtre Project, sélectionnez tous vos assets puis exportez-les dans un unitypackage via Export Package…
      • L’export peut prendre plusieurs dizaines de secondes, voire minutes, selon la taille du projet (mon package faisait 750 Mo !)
    4. Mettez votre package pas trop loin, on va s’en resservir souvent pendant le processus.
    5. Supprimez tous vos assets dans Project ! Quand je vous disais d’archiver le projet, ce n’était pas pour rien…
    6. Définissez Project Settings > Editor > Asset Serialization > Mode: Force Text
    7. Double-cliquez sur votre unitypackage depuis l’explorateur et attendez le chargement qui peut prendre du temps.
    8. Désélectionnez tout puis sélectionnez uniquement une partie du package pour l’import.
      • Le but est d’importer petit à petit les assets. À chaque import, Unity va re-sérialiser en texte.
      • Si l’import plante (et donc que Unity crash pour out of memory), c’est parce que vous avez pris trop de fichiers d’un coup. Dans ce cas, réimportez à nouveau en prenant moins d’assets.
      • Vous constaterez très probablement que la console vous signale des erreurs pour classe manquante. Dans ce cas, identifiez un asset ou un groupe d’asset qui contient la classe manquante et importez-le. Très important : lorsque vous avez réglé les problèmes de classes manquantes, il faut réimporter tous les assets à partir desquels l’erreur est apparue dans la console, toujours petit à petit (avec les mêmes sélections), qui sont déjà importés pour recréer les variables et les références qui ont pu se briser.
      • Pour vous aider dans le processus, je vous conseille d’avoir la fenêtre du Gestionnaire des tâches pour voir la taille que prend une sélection d’assets en import pour mieux découper les imports.
    9. Une fois que tout est importé, faites un test. Il ne devrait rien manquer si vous avez tout bien importé / réimporté / réréimporté / …
      • Si vous avez des problèmes de NullPointerException ou des incohérences de comportement dans le test, identifiez l’asset qui lève cette exception ou l’incohérence puis réimportez-le. Ne faites surtout pas la réassignation vous-même, le réimport est censé corriger les problèmes de références manquantes (Missing) et les valeurs qui n’ont pas été bien importées.

    Le plus long dans le processus a été, pour moi, d’attendre l’ouverture du unitypackage à chaque import. C’est pourquoi vous devez bien comprendre comment sont rangés vos assets pour bien découper vos imports. Je pense que le mieux est d’importer du plus générique au plus spécifique. Par exemple, importer les scripts des plugins sur lesquels s’appuie votre code, puis petit à petit, importer les prefabs et assets jusqu’à arriver à ceux utilisés dans les scènes. Les scènes doivent d’ailleurs être importés en dernier, il s’agit du bout de la chaîne.

    Voilà, j’espère que ça servira à quelqu’un. De mon côté, ça m’a permis de travailler avec Mercurial.