物惯(子到父节点)变换顺序原因和不同坐标系下的变换顺序详解

在基于同一个参考坐标系中(也就是子节点到父节点坐标系中的变换),对于一个基本变换,D3D中要先缩放->旋转->平移。OGL中要先平移->旋转->缩放,其中的原因解释?

OGL中是左乘矩阵,因此子节点变换到父节点是先平移后旋转缩放,才是正确的变换顺序。
关于先平移后旋转不是OGL中解释的每次是基于当前嵌套坐标系变换,而是因为变换的矩阵乘法顺序和乘法规则规定的。
D3D中先旋转后平移才能如描述期望的正确变换。

如果改为先平移后旋转,出现问题的原因不是因为绕父节点基向量变换(公转,D3D中可以考虑为基于当前坐标系绕父节点公转),也不是绕子节点基向量变换(基于本地坐标,OGL中的变换顺序平移->旋转->缩放是可以这样考虑的),旋转没有问题,但是平移出现了不同。 因为在子节点坐标系为当前变换基向量中,进行了基于父节点的平移向量,乘以每个当前基向量提取出了部分相对于父节点基向量的权重也就是列向量,进行平移,作为结果。 进行了基于将要变换到的坐标系(旋转后坐标系,而不是平移)的分解组合结果,进行基于父节点坐标系的变换(平移量)。解释为矩阵变换顺序和乘法规则导致的结果即可,且要理解在D3D矩阵乘法规则下,子节点变换到父节点顺序是:缩放->旋转->平移, OGL中相反(乘法顺序不同)。

缩放和投影会改变轴大小,旋转会改变轴的走向,平移改变物体本地坐标原点位置;更多的是他们的乘法规则,乘法顺序会得到不一样的结果,结果是否是期望的作为判定是否正确标准
物体做不基于本地坐标系的变换,需要先平移回本地放射坐标系中进行放射变换,再平移回去原来位置,实现变换,但是一般设计中都是美术设计好基于模型本地坐标系进行缩放旋转平移变换,再平移到世界坐标系中。

下图为证明过程:

物惯(子到父节点)变换顺序原因和不同坐标系下的变换顺序详解_第1张图片

OGL中也是要缩放->旋转->平移顺序才得到在父坐标系(例如世界)中描述的结果,但是因为OGL中矩阵表示是列式矩阵,所以连续变换的顺序是左乘当前矩阵(矩阵变换向量也是左乘向量)才相当于D3D中的右连乘,所以OGL中同一个坐标系中变换是平移->旋转->缩放矩阵连序左乘才得到正确结果。


故OGL视图模型变换中,先设置视图变换,然后再设置模型变换。但平移旋转缩放不是相对于相同的父坐标系(如世界)那么乘法顺序就另当别论了。


OGL中的连续骨骼节点变换,基于全局坐标系的思考,照样描述(D3D),实现代码用连续左乘(也确保了最终节点左乘在最左边)得到叶子节点的变换矩阵,左乘向量得结果。

OGL中连续变换在节点内也可以用本地坐标系统考虑,本地坐标系统会跟着物体变换,也就是先平移到一个位置,本地坐标系统也平移了这个位置,在进行旋转,然后基于旋转后的本地坐标系进行缩放,在节点之间平移累加是不分先后的直接累加即可

OGL中行星卫星绕恒星旋转示例:

/*
 * Copyright (c) 1993-2003, Silicon Graphics, Inc.
 * All Rights Reserved
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose and without fee is hereby granted, provided that the above
 * copyright notice appear in all copies and that both the copyright
 * notice and this permission notice appear in supporting documentation,
 * and that the name of Silicon Graphics, Inc. not be used in
 * advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.
 *
 * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS" AND
 * WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR
 * FITNESS FOR A PARTICULAR PURPOSE.  IN NO EVENT SHALL SILICON
 * GRAPHICS, INC.  BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT,
 * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
 * OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION, LOSS OF
 * PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF THIRD
 * PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC.  HAS BEEN ADVISED OF
 * THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE POSSESSION, USE
 * OR PERFORMANCE OF THIS SOFTWARE.
 *
 * US Government Users Restricted Rights 
 * Use, duplication, or disclosure by the Government is subject to
 * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph
 * (c)(1)(ii) of the Rights in Technical Data and Computer Software
 * clause at DFARS 252.227-7013 and/or in similar or successor clauses
 * in the FAR or the DOD or NASA FAR Supplement.  Unpublished - rights
 * reserved under the copyright laws of the United States.
 *
 * Contractor/manufacturer is:
 *	Silicon Graphics, Inc.
 *	1500 Crittenden Lane
 *	Mountain View, CA  94043
 *	United State of America
 *
 * OpenGL(R) is a registered trademark of Silicon Graphics, Inc.
 */

/*
 *  planet.c
 *  This program shows how to composite modeling transformations
 *  to draw translated and rotated models.
 *  Interaction:  pressing the d and y keys (day and year)
 *  alters the rotation of the planet around the sun.
 */
#include <GL/glut.h>
#include <stdlib.h>

static int year = 0, day = 0;
static int sateliteDay = 0;

void init(void) 
{
   glClearColor (0.0, 0.0, 0.0, 0.0);
   glShadeModel (GL_FLAT);
}

void display(void)
{
   glClear (GL_COLOR_BUFFER_BIT);
   glColor3f (1.0, 1.0, 1.0);
   // 备份下之前的视口,投影,视图变换
   glPushMatrix();
   // 绘制太阳
   glutWireSphere(1.0, 20, 16);   /* draw sun */
   // 1.D3D中解释是量是父坐标的,缩放改变轴大小(沿着父坐标受轴走向原点影响),旋转改变轴走向(绕父坐标受原点影响),
   // 平移改变原点(在父坐标上不受缩放旋转影响)。其实按照行主序的矩阵,乘法结果是基于当前坐标系作父坐标值在当前坐标系相对于父坐标值分解权重变换。
   // 其实行主序矩阵中,在父坐标系描述的值,进行期望的矩阵累积变换就是:缩放旋转平移的顺序。
   // OGL中列式矩阵刚好是行矩阵的转置,乘法顺序不变,故在父坐标值描述中,进行期望的矩阵累积变换顺序是:平移旋转缩放,
   // 刚好可以解释为如果每次都是基于当前坐标系的,且变换的量也是基于当前坐标系进行的(其实是等于父坐标中的量)。

   // 2.子坐标值变换到父坐标,一个完整的变换(包含缩放旋转平移)完成后会得到一个向量(位移)(无论是重下往上,还是重上往下), 接着嵌套坐标计算,
   // 结果位移累加即可。父坐标值变换到子坐标(坐标参考系改变了)作子坐标变换到父坐标的逆变换即可。在完整变换之间D3D和OGL是一样的。

   // 3.故第一个glRotatef是绕太阳系公转,glTranslatef是在公转坐标系中平移,第二个glRotatef是平移后旋转倾斜一个固定角度,
   // 第三个glRotatef是绕倾斜固定角度后旋转(自转)。

   // 4.矩阵的连乘可以改变本地坐标系(轴大小走向,平移除外),所以:
   // glRotatef((GLfloat)day-23.5, 0.0, 1.0, 0.0);不能代替两个glRotatef变换。

   // 绘制行星
   glRotatef((GLfloat)year, 0.0, 1.0, 0.0);
   glTranslatef (2.0, 0.0, 0.0);
   glRotatef((GLfloat)-23.5, 0.0, 0.0, 1.0);
   glRotatef((GLfloat)day, 0.0, 1.0, 0.0);
   glutWireSphere(0.2, 10, 8);    /* draw smaller planet */

   // 绘制卫星1
   glPushMatrix();
   // 绕行星公转,D3D解释是TR变换,平移到了一个位置,旋转是绕平移之前的父坐标旋转所以是公转,
   // 公转的量不是基于平移后的坐标,也不是父坐标,而是父坐标值在旋转后的权重累积。
   // OGL解释用本地坐标系思考,旋转是基于当前坐标的,量也是当前坐标的;平移是旋转后的,无论旋转到什么角度都是平移这么多。
   // 其实在一个完整变换内部OGL的思维更加简单一些。
   glRotatef((GLfloat)sateliteDay, 0.0, 1.0, 0.0);
   glTranslatef(0.5, 0.0, 0.0);
   glutWireSphere(0.1, 5, 4);    /* draw satelite1 */
   glPopMatrix();

   // 绘制卫星2
   glPushMatrix();
   // 绕行星公转
   glRotatef((GLfloat)sateliteDay + 180, 0.0, 1.0, 0.0);
   glTranslatef(0.5, 0.0, 0.0);
   glutWireSphere(0.1, 5, 4);    /* draw satelite2 */
   glPopMatrix();
   // 5. 恢复视口,投影,视图变换变换;避免下一次display取得不是想要的结果。
   glPopMatrix();

   glutSwapBuffers();
}

void reshape (int w, int h)
{
   glViewport (0, 0, (GLsizei) w, (GLsizei) h); 
   glMatrixMode (GL_PROJECTION);
   glLoadIdentity ();
   gluPerspective(60.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   gluLookAt (0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
}

void keyboard (unsigned char key, int x, int y)
{
   switch (key) {
   case 's':
	   sateliteDay = (sateliteDay + 10) % 360;
	   glutPostRedisplay();
	   break;
   case 'S':
	   sateliteDay = (sateliteDay - 10) % 360;
	   glutPostRedisplay();
	   break;
      case 'd':
         day = (day + 10) % 360;
         glutPostRedisplay();
         break;
      case 'D':
         day = (day - 10) % 360;
         glutPostRedisplay();
         break;
      case 'y':
         year = (year + 5) % 360;
         glutPostRedisplay();
         break;
      case 'Y':
         year = (year - 5) % 360;
         glutPostRedisplay();
         break;
      case 27:
         exit(0);
         break;
      default:
         break;
   }
}

int main(int argc, char** argv)
{
   glutInit(&argc, argv);
   glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
   glutInitWindowSize (500, 500); 
   glutInitWindowPosition (100, 100);
   glutCreateWindow (argv[0]);
   init ();
   glutDisplayFunc(display); 
   glutReshapeFunc(reshape);
   glutKeyboardFunc(keyboard);
   glutMainLoop();
   return 0;
}


OGL机器手臂例子(骨骼动画):
/*
 * Copyright (c) 1993-2003, Silicon Graphics, Inc.
 * All Rights Reserved
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose and without fee is hereby granted, provided that the above
 * copyright notice appear in all copies and that both the copyright
 * notice and this permission notice appear in supporting documentation,
 * and that the name of Silicon Graphics, Inc. not be used in
 * advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.
 *
 * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS" AND
 * WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR
 * FITNESS FOR A PARTICULAR PURPOSE.  IN NO EVENT SHALL SILICON
 * GRAPHICS, INC.  BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT,
 * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
 * OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION, LOSS OF
 * PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF THIRD
 * PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC.  HAS BEEN ADVISED OF
 * THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE POSSESSION, USE
 * OR PERFORMANCE OF THIS SOFTWARE.
 *
 * US Government Users Restricted Rights 
 * Use, duplication, or disclosure by the Government is subject to
 * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph
 * (c)(1)(ii) of the Rights in Technical Data and Computer Software
 * clause at DFARS 252.227-7013 and/or in similar or successor clauses
 * in the FAR or the DOD or NASA FAR Supplement.  Unpublished - rights
 * reserved under the copyright laws of the United States.
 *
 * Contractor/manufacturer is:
 *	Silicon Graphics, Inc.
 *	1500 Crittenden Lane
 *	Mountain View, CA  94043
 *	United State of America
 *
 * OpenGL(R) is a registered trademark of Silicon Graphics, Inc.
 */

/*
 * robot.c
 * This program shows how to composite modeling transformations
 * to draw translated and rotated hierarchical models.
 * Interaction:  pressing the s and e keys (shoulder and elbow)
 * alters the rotation of the robot arm.
 */
#include <GL/glut.h>
#include <stdlib.h>

static int shoulder = 0, elbow = 0;

void init(void) 
{
   glClearColor (0.0, 0.0, 0.0, 0.0);
   glShadeModel (GL_FLAT);
}

void display(void)
{
   glClear (GL_COLOR_BUFFER_BIT);
   glPushMatrix();
   // 1.OGL中单一整体变换内部,平移旋转缩放,变换的是基于本地坐标系进行指定的变换量来变换物体,如果没有绘制物体,那么变换的是
   // 本地坐标系,下一个变换基于本地坐标系进行指定的值(刚好相等)来变换。
   glTranslatef (-1.0, 0.0, 0.0);
   glRotatef ((GLfloat) shoulder, 0.0, 0.0, 1.0);
   glTranslatef (1.0, 0.0, 0.0);
   // 2.防止影响到下一步的变换,用glPushMatrix,glPopMatrix来保护。
   glPushMatrix();
   glScalef (2.0, 0.4, 1.0);
   glutWireCube (1.0);
   glPopMatrix();

   // 3.两个整体变换之间,也是基于前面的本地坐标系(这里是父坐标系),来进行变换。D3D中解释为物体在父坐标中的变换,OGL中也一样。
   // 如果要求得子坐标中的值在父坐标中表示,父节点直接乘以TRS变换(D3D中是SRT)。已知子节点在父节点坐标系中的值,表示为子节点上的
   // 相对位置,乘以合并矩阵的逆即可。(和视图变换一样)
   // U3D中节点之间世界坐标系是左手坐标系,所以已知子节点的世界坐标,求取子节点的本地坐标,Vlocal * SRT= Vworld.
   // Vlocal = Vworld * (T^-1R^-1S^-1); 如果是OGL中Vlocal = Vworld * (S^-1R^-1T^-1);
   // 解决这些问题关键是看假设已知变量,建立联系式子,先理论推导,然后实际操作验证,还要寻找一些相对位置关系,摄像机空间,屏幕位置,
   // 框架提供的函数,来解决问题。
   glTranslatef (1.0, 0.0, 0.0);
   glRotatef ((GLfloat) elbow, 0.0, 0.0, 1.0);
   glTranslatef (1.0, 0.0, 0.0);
   glPushMatrix();
   glScalef (2.0, 0.4, 1.0);
   glutWireCube (1.0);
   glPopMatrix();

   glPopMatrix();
   glutSwapBuffers();
}

void reshape (int w, int h)
{
   glViewport (0, 0, (GLsizei) w, (GLsizei) h); 
   glMatrixMode (GL_PROJECTION);
   glLoadIdentity ();
   gluPerspective(65.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   glTranslatef (0.0, 0.0, -5.0);
}

void keyboard (unsigned char key, int x, int y)
{
   switch (key) {
      case 's':
         shoulder = (shoulder + 5) % 360;
         glutPostRedisplay();
         break;
      case 'S':
         shoulder = (shoulder - 5) % 360;
         glutPostRedisplay();
         break;
      case 'e':
         elbow = (elbow + 5) % 360;
         glutPostRedisplay();
         break;
      case 'E':
         elbow = (elbow - 5) % 360;
         glutPostRedisplay();
         break;
      case 27:
         exit(0);
         break;
      default:
         break;
   }
}

int main(int argc, char** argv)
{
   glutInit(&argc, argv);
   glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
   glutInitWindowSize (500, 500); 
   glutInitWindowPosition (100, 100);
   glutCreateWindow (argv[0]);
   init ();
   glutDisplayFunc(display); 
   glutReshapeFunc(reshape);
   glutKeyboardFunc(keyboard);
   glutMainLoop();
   return 0;
}


U3D中摄像机位置设置变换的例子:
--U3D中的世界坐标是左手坐标系的。    
--Transform坐标系下的当前Pos是相对于父坐标系的,但旋转缩放都是相对于当前Pos的(也就是相对于父坐标系),其实所有的变换都是一个坐标系连续变换或两个坐标系之间的。
   --所以Transform坐标系下的子Pos点需要进行直接缩放旋转变换即得Transform的父坐标系变换Pos,该变换Pos加上Transform的Pos就是父坐标系的Pos.
   --子节点和父节点的坐标关系,子节点的Rotation和Scale只是影响它坐标系上的物体,子节点本身的坐标原点为position是相对于父坐标系的。
   --因此子节点的位置,转换到父节点的父节点位置就需要(其实是惯性坐标系下的直接变换):Vparentparent = POSchild(其实为Parent坐标系下的Vector) *  Sparent *Rparent+ Tparent
   --而父节点中的位置转换到子节点就需要: POSchild = (Vparentparent - Tparent^-1)*Rparent^-1*Sparent^-1
其实等于: POSchild = Vparentparent * (Tparent^-1 *Rparent^-1*Sparent^-1)
   --节点之间坐标系的嵌套也是类似的,递推即可。
   -- 其实缩放是向量的话(斜对角线的矩阵,转置一样),那么先缩放后旋转,和先旋转后缩放都没区别了,因为:(V*S)*R = V*k*R和 (V*R)*S = V*R*k = V*k*R是可交换的。
   -- 遇到更多问题或者代码时候再研究。

实例:
  // z值要设置正确
        Vector3 screenCenterVec = Camera.main.WorldToScreenPoint(mapCenter);
        Vector3 screenWorldWidthVec = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width * 0.5f, Screen.height, screenCenterVec.z));
        Vector3 screenWorldWidthVecOrigin = Camera.main.ScreenToWorldPoint(new Vector3(0, Screen.height, screenCenterVec.z));
        // 需要找到相对位置
        screenWorldWidthVec = screenWorldWidthVec - screenWorldWidthVecOrigin;

using UnityEngine;
using System.Collections;

// 本地坐标转换到世界坐标系中
public class TranslatePosition : MonoBehaviour {

    void Start () {
 
        Transform cameraCtrl = GameObject.Find("CameraCtrl").transform;
        if (cameraCtrl == null) return;

        Transform camera = cameraCtrl.FindChild("Camera").transform;
        Vector3 destPos = camera.position;

        Vector3 worldPos = cameraCtrl.localRotation * camera.localPosition;
        worldPos += cameraCtrl.localPosition;
       worldPos += Vector3.zero;
    }

    void Update () {
    }
}

 using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using System;
// 本模块适应用摄像机的可到达的世界边界和小地图的边界等比例对应,所以摄像机调整位置小地图比例和触摸框都可以自适应。
// 比例转换思路:转换到摄像机坐标系中,只考虑绕y轴的旋转, 可以通过摄像机的旋转求得(取逆)。
// 将摄像机坐标系中的x轴长度和小地图上的smalllMapWidth得到比例关系,而z轴上的对应关系也可以确定了;y和z轴旋转会影响x值,因为这里的z轴旋转很小可以忽略。
// 注意:从屏幕空间获取到世界位置大小时候,z值要设置正确,而且 需要用相对位置来计算。
public class UISmallMapLogic : MonoBehaviour {
    class mapItem
    {
       public bool mIsRed;
       public int nType;
       public GameObject mSoldierObj;
       public Transform mSoldierMapPoint;
    }
    // item
    Dictionary<int, mapItem> mMapItems = new Dictionary<int, mapItem>();
    List<mapItem> mNoUseItems = new List<mapItem>();
    List<int> mRemoveTemp = new List<int>();
    // prefab
    List<Dictionary<int, string>> mItemPrefab = new List<Dictionary<int, string>>();

    Vector3 mLeftBorderCameraPos;
    Vector3 mRightBorderCameraPos;
    float mSinMainCamera;
    float mCosMainCamera;
    float mCameraSpaceToSmallMapScale;

    RectTransform mLeftBorderRtTrans;
    RectTransform mRightBorderRtTrans;
    Transform mBgTrans;
    float mSmallMapHalfWidth;
    float mSmallMapHalfHight;

    // Use this for initialization
    void Awake()
    {
        mBgTrans = this.transform.FindChild("img_top_bg");
        mLeftBorderRtTrans = this.transform.FindChild("img_top_bg/img_red_base_point").GetComponent<RectTransform>();
        mRightBorderRtTrans = this.transform.FindChild("img_top_bg/img_blue_base_point").GetComponent<RectTransform>();
        mItemPrefab.Clear();
        Dictionary<int, string> itemRed = new Dictionary<int, string>();
        Dictionary<int, string> itemBlue = new Dictionary<int, string>();
        mItemPrefab.Add(itemRed);
        mItemPrefab.Add(itemBlue);

    }

    void Start() {

    }

    void OnDisable()
    {
        Release();
    }

    // Update is called once per frame
    void Update()
    {
        // update SmallMap point position
        foreach (KeyValuePair<int, mapItem> item in mMapItems)
        {
            if (!item.Value.mSoldierObj.activeSelf)
            {
                mNoUseItems.Add(item.Value);
                item.Value.mSoldierMapPoint.gameObject.SetActive(false);
                //mMapItems.Remove(item.Key);
                mRemoveTemp.Add(item.Key);
            }
            else
            {
                Vector2 vecAnchorPos = GetMapItemPosFromSoldierPos(item.Value.mSoldierObj, item.Value.mIsRed);
                if (vecAnchorPos.x < -mSmallMapHalfWidth || vecAnchorPos.x > mSmallMapHalfWidth || vecAnchorPos.y < -mSmallMapHalfHight || vecAnchorPos.y > mSmallMapHalfHight)
                {
                    item.Value.mSoldierMapPoint.gameObject.SetActive(false);
                }
                else
                {
                    item.Value.mSoldierMapPoint.GetComponent<RectTransform>().anchoredPosition = vecAnchorPos;
                    item.Value.mSoldierMapPoint.gameObject.SetActive(true);
                }
            }
        }

        // remove unUse
        foreach (int key in mRemoveTemp)
        {
            mMapItems.Remove(key);
        }
    }

#region Public interface
    public void SetPrefabPath(bool isRed,  int idx, string strPrefab)
    {
        int index = 0;
        if(!isRed)
        {
            index = 1;
        }
        Dictionary<int, string> mRefDic = mItemPrefab[index];
        mRefDic.Add(idx, strPrefab);
    }

    //public void SetCameraBorderInfo(Vector3 vecWorldLeft, Vector3 vecWorldRight, float smallMapHeight, float smallMapWidth)
    //{
    //    float relateX = (float)Math.Abs(vecWorldRight.x - vecWorldLeft.x);
    //    float relateZ = (float)Math.Abs(vecWorldRight.z - vecWorldLeft.z);
    //    float relateLen = (float)Math.Sqrt(relateX * relateX + relateZ * relateZ);

    //    mCosMainCamera = relateX / relateLen;
    //    mSinMainCamera = relateZ / relateLen;

    //    mLeftBorderCameraPos = GetCameraSpacePosByRotate(vecWorldLeft);
    //    mRightBorderCameraPos = GetCameraSpacePosByRotate(vecWorldRight);

    //    // 直接映射得比例,避免在主摄像机和UI摄像机之间运算
    //    float fMapWidth = Math.Abs(mLeftBorderRtTrans.anchoredPosition.x - mRightBorderRtTrans.anchoredPosition.x);
    //    float fCameraBorderWidth = Math.Abs(mRightBorderCameraPos.x - mLeftBorderCameraPos.x);
    //    mCameraSpaceToSmallMapScale = (float)Math.Abs(fMapWidth / fCameraBorderWidth);

    //    mSmallMapHalfWidth = smallMapWidth * 0.5f;
    //    mSmallMapHalfHight = smallMapHeight * 0.5f;
    //}

    Vector3 GetCameraToWorldPos(Vector3 vecCameraNodePos)
    {
        Transform cameraCtrl = GameObject.Find("CamPosCtrl").transform;
        //Transform cameraNode = Camera.main.transform; //GameObject.Find("CamPosCtrl/mainCamera").transform;
        if(cameraCtrl == null)
        {
            GSELog.LogErr("UISmallMapLogic error, CamPosCtrl GameObject is not exist.");
        }

        Vector3 cameraWorldPos = cameraCtrl.rotation * vecCameraNodePos;// cameraNode.localPosition;
        cameraWorldPos += cameraCtrl.localPosition;

        return cameraWorldPos;
    }



    public float InitBaseSpaceInfo(Vector3 mapCenter, Vector3 vecCamraLeft, Vector3 vecCameraRight, float smallMapHeight, float smallMapWidth)
    {
        // 思路是将摄像机坐标系中的x轴长度和小地图上的smalllMapWidth得到比例关系,而z轴上的对应关系也可以确定了;y和z轴旋转会影响x值,因为这里的z轴旋转很小可以忽略。
        float rotateY = Camera.main.transform.rotation.eulerAngles.y * Mathf.Deg2Rad;
        mCosMainCamera = (float)Math.Cos(rotateY);
        mSinMainCamera = (float)Math.Sin(rotateY);

        // 求得CameraWidth
        Vector3 cameraLeftWorldPos = GetCameraToWorldPos(vecCamraLeft);
        Vector3 cameraRightWorldPos = GetCameraToWorldPos(vecCameraRight);

        Vector3 cameraLeftRotateVec = GetCameraSpacePosByRotate(cameraLeftWorldPos);
        Vector3 cameraRightRotateVec = GetCameraSpacePosByRotate(cameraRightWorldPos);

        // z值要设置正确
        Vector3 screenCenterVec = Camera.main.WorldToScreenPoint(mapCenter);
        Vector3 screenWorldWidthVec = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width * 0.5f, Screen.height, screenCenterVec.z));
        Vector3 screenWorldWidthVecOrigin = Camera.main.ScreenToWorldPoint(new Vector3(0, Screen.height, screenCenterVec.z));
        // 需要找到相对位置
        screenWorldWidthVec = screenWorldWidthVec - screenWorldWidthVecOrigin;

        float cameraSpaceMinX = cameraLeftRotateVec.x - screenWorldWidthVec.x;
        float cameraSpaceMaxX = cameraRightRotateVec.x + screenWorldWidthVec.x;

        Vector3 mapCenterVec = GetCameraSpacePosByRotate(mapCenter);
        float cameraSpaceZ = mapCenterVec.z;
        //Vector3 cameraSpaceLeftVec = new Vector3()

        mLeftBorderCameraPos = new Vector3(cameraSpaceMinX, 0, cameraSpaceZ);
        mRightBorderCameraPos = new Vector3(cameraSpaceMaxX, 0, cameraSpaceZ);

        // 直接映射得比例,避免在主摄像机和UI摄像机之间运算
        float fSmallMapWidth = Math.Abs(mLeftBorderRtTrans.anchoredPosition.x - mRightBorderRtTrans.anchoredPosition.x);
        float fCameraBorderWidth = Math.Abs(mRightBorderCameraPos.x - mLeftBorderCameraPos.x);
        mCameraSpaceToSmallMapScale = (float)Math.Abs(fSmallMapWidth / fCameraBorderWidth);

        mSmallMapHalfWidth = smallMapWidth * 0.5f;
        mSmallMapHalfHight = smallMapHeight * 0.5f;

        float fScreenWidthScale = (float)(2 * screenWorldWidthVec.x) / (float)Math.Abs(cameraSpaceMaxX - cameraSpaceMinX);
        return smallMapWidth * fScreenWidthScale;
    }


    public void AddMapItem(int netId, GameObject soldierObj, bool isRed, int nType)
    {
        mapItem itemExist = null;
        if (mMapItems.TryGetValue(netId, out itemExist))
        {
            return;
        }

        mapItem item = GetNoUseItem(isRed, nType);
        if (item == null)
        {
            item = new mapItem();
            item.mIsRed = isRed;
            item.nType = nType;
            string strPrefab = GetPrefabPath(isRed, nType);
            item.mSoldierMapPoint = BGObjectPoolManager.Instance.Instantiate(strPrefab).transform;
        }

        item.mSoldierObj = soldierObj;
        item.mSoldierMapPoint.localScale = new Vector3(1, 1, 1);
        item.mSoldierMapPoint.SetParent(mBgTrans, false);
        // 设置位置
        item.mSoldierMapPoint.GetComponent<RectTransform>().anchoredPosition = GetMapItemPosFromSoldierPos(item.mSoldierObj, item.mIsRed);
        // 显示point
        item.mSoldierMapPoint.gameObject.SetActive(true);
        mMapItems.Add(netId, item);
    }

    public void DeleteMapItem(int netId)
    {
        mapItem item = null;
        if( mMapItems.TryGetValue(netId, out item) )
        {
            mNoUseItems.Add(item);
            item.mSoldierMapPoint.gameObject.SetActive(false);
            mMapItems.Remove(netId);
        }
    }

    public void Release()
    {
        mMapItems.Clear();
        mNoUseItems.Clear();
        mRemoveTemp.Clear();
        mItemPrefab.Clear();
        mLeftBorderCameraPos = Vector3.zero;
        mRightBorderCameraPos = Vector3.zero;
        mSinMainCamera = 0.0f;
        mCosMainCamera = 0.0f;
        mCameraSpaceToSmallMapScale = 0.0f;
        mLeftBorderRtTrans = null;
        mRightBorderRtTrans = null;
        mBgTrans = null;
        mSmallMapHalfWidth = 0.0f;
        mSmallMapHalfHight = 0.0f;
    }
#endregion

#region Logic Detail
    string GetPrefabPath(bool isRed, int nType)
    {
        int idx = 0;
        if (!isRed)
        {
            idx = 1;
        }
        Dictionary<int, string> mRefDic = mItemPrefab[idx];
        string strPrefab;
        if (mRefDic.TryGetValue(nType, out strPrefab))
        {
            return strPrefab;
        }

        return "";
    }
    // 摄像机在世界坐标系的逆旋转
    Vector3 GetCameraSpacePosByRotate(Vector3 worldPos)
    {
        return new Vector3(worldPos.x * mCosMainCamera - worldPos.z * mSinMainCamera, worldPos.y, worldPos.x * mSinMainCamera + worldPos.z * mCosMainCamera);
    }

    Vector2 GetMapItemPosFromSoldierPos(GameObject soldierObj, bool isRed)
    {
        Vector3 relateCameraPos = GetCameraSpacePosByRotate(soldierObj.transform.position);
        Vector3 relatePos = relateCameraPos - mLeftBorderCameraPos;
        RectTransform rtTrans = mLeftBorderRtTrans;
        if (!isRed)
        {
            relatePos = relateCameraPos - mRightBorderCameraPos;
            rtTrans = mRightBorderRtTrans;
        }
        Vector3 smallMapRelatePos = mCameraSpaceToSmallMapScale * relatePos;
        return new Vector2(rtTrans.anchoredPosition.x + smallMapRelatePos.x, rtTrans.anchoredPosition.y + smallMapRelatePos.z);//+ 
    }

    // avoid new MapItem everySoldier Add
    mapItem GetNoUseItem(bool isRed, int nType)
    {
        foreach (mapItem item in mNoUseItems)
        {
            if (item.mIsRed == isRed && item.nType == nType)
            {
                mapItem noUseItem = item;
                mNoUseItems.Remove(item);
                return noUseItem;
            }
        }
        return null;
    }


#endregion
}



你可能感兴趣的:(物惯(子到父节点)变换顺序原因和不同坐标系下的变换顺序详解)