《红孩儿引擎内功心法修练与Cocos2d-x》之结点系统(场景,层,精灵)(精)

vscode中运行gcc -v命令失败了,提示:gcc不是内部或外部命令...如何解决? 🏆本文收录于 《全栈Bug调优(实战版)》 专栏,该专栏专注于分享我在真实项目开发中遇到的各类疑难Bug及其深层成因,并系统提供高效、可复现的解决思路和实操方案。无论你是刚入行的新手开发者,还是拥有多年项目经验的资深工程师,本专栏都将为你提供一条系统化、高质量的问题排查与优化路径,助力你加速成长,攻克技术壁垒,迈向技术价值最大化与职业发展的更高峰🚀! 阅读详情

http://blog.youkuaiyun.com/honghaier/article/details/8760124点击打开链接


 

[Cocos2d-x相关教程来源于红孩儿的游戏编程之路优快云博客地址:http://blog.youkuaiyun.com/honghaier

红孩儿Cocos2d-X学习园地QQ3群:205100149,47870848

礼物:《红孩儿引擎内功心法修练与Cocos2d-x》之结点系统(场景,层,精灵)


          另:好吧,我彻底被Cocos2d-x版本的更新速度打败了!我决定停止再对2.0.2版本再做深入分析讲解。本博文实为新书的节选,做为礼物送给大家,望各位仔细阅读后提出批评!
      另:本周末工具箱会更新到0.1.1版本,欢迎下载并到官方论坛提出BUG反馈和使用建议。
  

   

本节的学习目标:

(1)    了解结点系统,学会自行构建结点系统。

(2)    了结场景,层,精灵的组织关系与各自功能



2.1 结点系统原理入门

2.1.1 结点启蒙:

在介绍Cocos2d-x的结点系统之前,我们需要首先做一些启蒙,什么是树?


定义:

 

一棵树(tree)是由n(n>0)个元素组成的有限集合,其中:

(1)每个元素称为结点(node);

(2)有一个特定的结点,称为根结点或根(root);

(3)除根结点外,其余结点被分成m(m>=0)个互不相交的有限集合,而每个子集又都是一棵树(称为原树的子树)

 

如图A:



对于树结构有几个概念要记一下:

度:树的度——也即是宽度,简单地说,就是结点的分支数。以组成该树各结点中最大的度作为该树的度,如上图的树,其度为3;树中度为零的结点称为叶结点或终端结点。树中度不为零的结点称为分枝结点或非终端结点。除根结点外的分枝结点统称为内部结点。

深度:树的深度——组成该树各结点的最大层次,如上图,其深度为3;

层次:根结点的层次为1,其他结点的层次等于它的父结点的层次数加1.

         请仔细观察上图这棵树,这里A是根结点,其余结点均是属于A的不同层级的子结点。我们由此图进一步进行想像,人的身体其实也是一棵树。

如图B:



 

         上图详细表现了人体树结构的组织结构,左边是人形的结构,右边是层级关系展开。它作为骨骼动画的基础理论被广泛的应用于各类游戏动画中。

         我们看一下这张图,首先有一个根骨(脊椎),这个根骨即是树结构中的根结点。根骨下有三根子骨骼(左胯,右胯,颈背),这三根子骨骼也各自有属于自已的子骨骼树结构,同时它们由父骨骼牵引并牵引着子骨骼,与父骨骼和第一层子骨骼保持着固定的距离。

         试想一下:

 

         当我们想把这个人移动到一个位置点时,只需要把根骨移动到相应位置,即这个人的所有骨骼都会被这种牵引关系移动到这个世界位置的相对骨骼位置。但如果我们把左胯这根骨骼去掉的话,则在移动根骨后,左胯仍停留在原地,它已经不再属于当前骨骼树了,而成了一棵独立的骨骼树。

        

         看完这张图,已经比较接近我们所要讲述的内容了,对于骨骼结构的理解将有助于我们掌握远大于骨骼动画本身的结构模式,因为由此理论基础我们将学会一切基于结点树结构的系统。

 

         下面我们来用C++的代码构建这样一套系统。

首先,我们创建一个基类,称之为结点。

[cpp]  view plain copy
  1. //结点类  
  2. class CNode  
  3. {  
  4. public:  
  5.   
  6.     //构造  
  7.     CNode();  
  8.     //析构  
  9.     virtual ~CNode();  
  10.   
  11. public:  
  12.   
  13.     //更新  
  14.     virtual  inline void    Update();  
  15.     //渲染  
  16.     virtual  inline void    Draw();  
  17.   
  18. public:  
  19.     //设置当前结点名称  
  20.     void    SetName(const char* szName);  
  21.     //取得当前结点名称  
  22.     const string&   GetName();  
  23.   
  24.     //加入一个子结点类  
  25.     void    AddChild(CNode* pChildNode);  
  26.     //取得子结点  
  27.     CNode*  GetFirstChild();  
  28.   
  29.     //加入一个兄弟结点类  
  30.     void    AddBorther(CNode* pBortherNode);  
  31.     //取得兄弟结点  
  32.     CNode*  GetFirstBorther();  
  33.   
  34.     //删除一个结点  
  35.     bool    DelNode(CNode*  pNode);  
  36.     //清空所有子结点  
  37.     void    DelAllChild();  
  38.     //清空所有兄弟结点  
  39.     void    DelAllBorther();  
  40.   
  41.   
  42.     //查询某个子结点-- 纵向查询  
  43.     CNode*  QueryChild(const char* szName);  
  44.     //查询某个兄弟结点-- 横向查询  
  45.     CNode*  QueryBorther(const char* szName);  
  46.   
  47.   
  48.     //为了方便检测结点树系统的创建结果,这里增加了一个保存结点树到XML文件的函数。  
  49.     bool    SaveNodeToXML(const char* szXMLFile);  
  50.   
  51. protected:  
  52.   
  53.     //设置父结点  
  54.     void    SetParent(CNode* pParentNode);  
  55.     //取得父结点  
  56.     CNode*  GetParent();  
  57.   
  58.     //保存结点树到XML文件,这个函数是只生成本结点的信息。  
  59.     bool    SaveNodeToXML(FILE* hFile);  
  60. private:  
  61.   
  62.     //当前结点名称  
  63.     string  m_strNodeName;  
  64.     //父结点  
  65.     CNode*  m_pParentNode;  
  66.     //第一个子结点  
  67.     CNode*  m_pFirstChild;  
  68.     //第一个兄弟结点  
  69.     CNode*  m_pFirstBorther;  
  70. }  
  71. ;  

对应的实现:

[cpp]  view plain copy
  1. #include "Node.h"  
  2. //构造  
  3. CNode::CNode()  
  4. {  
  5.     m_strNodeName[0] = '\0';  
  6.     m_pParentNode = NULL;  
  7.     m_pFirstChild = NULL;  
  8.     m_pFirstBorther = NULL;  
  9. }  
  10.   
  11. //析构  
  12. CNode::~CNode()  
  13. {  
  14.     DelAllChild();  
  15. }  
  16.   
  17. //更新  
  18. void    CNode::Update()  
  19. {  
  20.     if(m_pFirstChild)  
  21.     {  
  22.         m_pFirstChild->Update();  
  23.     }  
  24.     //在这里增加你更新结点的处理  
  25.     …  
  26.     if(m_pFirstBorther)  
  27.     {  
  28.         m_pFirstBorther->Update();  
  29.     }  
  30. }  
  31. //直接渲染  
  32. void    CNode::Draw()  
  33. {  
  34.     if(m_pFirstChild)  
  35.     {  
  36.         m_pFirstChild->Draw();  
  37.     }  
  38.     //在这里增加你渲染图形的处理  
  39.     …  
  40.     if(m_pFirstBorther)  
  41.     {  
  42.         m_pFirstBorther->Draw();  
  43.     }  
  44. }  
  45. //设置当前结点名称  
  46. void    CNode::SetName(const char* szName)  
  47. {  
  48.     m_strNodeName = szName ;  
  49. }  
  50. //取得当前结点名称  
  51. const string& CNode::GetName()  
  52. {  
  53.     return m_strNodeName;  
  54. }  
  55. //加入一个子结点类  
  56. void    CNode::AddChild(CNode*  pChildNode)  
  57. {  
  58.     if(pChildNode)  
  59.     {  
  60.         if(m_pFirstChild)  
  61.         {  
  62.             m_pFirstChild->AddBorther(pChildNode);  
  63.         }  
  64.         else  
  65.         {  
  66.             m_pFirstChild = pChildNode;  
  67.             m_pFirstChild->SetParent(this);  
  68.         }  
  69.     }  
  70. }  
  71. //取得子结点  
  72. CNode*  CNode::GetFirstChild()  
  73. {  
  74.     return m_pFirstChild ;  
  75. }  
  76.   
  77. //加入一个兄弟结点类  
  78. void    CNode::AddBorther(CNode* pBortherNode)  
  79. {  
  80.     if(pBortherNode)  
  81.     {  
  82.         if(m_pFirstBorther)  
  83.         {  
  84.             m_pFirstBorther->AddBorther(pBortherNode);  
  85.         }  
  86.         else  
  87.         {  
  88.             m_pFirstBorther = pBortherNode;  
  89.             m_pFirstBorther->SetParent(m_pParentNode);  
  90.         }  
  91.     }  
  92. }  
  93. //取得兄弟结点  
  94. CNode*  CNode::GetFirstBorther()  
  95. {  
  96.     return m_pFirstBorther ;  
  97. }  
  98. //删除一个子结点类  
  99. bool    CNode::DelNode(CNode*   pTheNode)  
  100. {  
  101.     if(pTheNode)  
  102.     {  
  103.         if(m_pFirstChild)  
  104.         {  
  105.             if(m_pFirstChild == pTheNode)  
  106.             {  
  107.                 m_pFirstChild = pTheNode->GetFirstBorther();  
  108.                 delete pTheNode;  
  109.                 return true;  
  110.             }  
  111.             else  
  112.             {  
  113.                 if(true == m_pFirstChild->DelNode(pTheNode))return true;  
  114.             }  
  115.         }  
  116.         if(m_pFirstBorther)  
  117.         {  
  118.             if(m_pFirstBorther == pTheNode)  
  119.             {  
  120.                 m_pFirstBorther = pTheNode->GetFirstBorther();  
  121.                 delete pTheNode;  
  122.                 return true;  
  123.             }  
  124.             else  
  125.             {  
  126.                 if(true == m_pFirstBorther->DelNode(pTheNode))return true;  
  127.             }  
  128.         }  
  129.     }  
  130.     return false;  
  131. }  
  132. //清空所有子结点  
  133. void    CNode::DelAllChild()  
  134. {  
  135.     if(m_pFirstChild)  
  136.     {  
  137.         CNode * pBorther = m_pFirstChild->GetFirstBorther();  
  138.         if(pBorther)  
  139.         {  
  140.             pBorther->DelAllBorther();  
  141.             delete pBorther;  
  142.             pBorther = NULL;  
  143.         }  
  144.         delete m_pFirstChild ;  
  145.         m_pFirstChild = NULL;  
  146.     }  
  147. }  
  148.   
  149. //清空所有兄弟结点  
  150. void    CNode::DelAllBorther()  
  151. {  
  152.     if(m_pFirstBorther)  
  153.     {  
  154.         m_pFirstBorther->DelAllBorther();  
  155.         delete m_pFirstBorther;  
  156.         m_pFirstBorther = NULL;  
  157.     }  
  158. }  
  159.   
  160. //查询某个子结点-- 纵向查询  
  161. CNode*  CNode::QueryChild(const char* szName)  
  162. {  
  163.     if(szName)  
  164.     {  
  165.         if(m_pFirstChild)  
  166.         {  
  167.             //如果是当前子结点,返回子结点。  
  168.             if(0 == strcmp(szName,m_pFirstChild->GetName().c_str()))  
  169.             {  
  170.                 return m_pFirstChild;  
  171.             }  
  172.             else  
  173.             {  
  174.                 //如果不是,查询子结点的子结点。  
  175.                 CNode*  tpChildChild = m_pFirstChild->QueryChild(szName);  
  176.                 if(tpChildChild)  
  177.                 {  
  178.                     return tpChildChild ;  
  179.                 }  
  180.                 //如果还没有,查询子结点的兄弟结点。  
  181.                 return  m_pFirstChild->QueryBorther(szName);  
  182.             }  
  183.         }  
  184.     }  
  185.     return NULL;  
  186. }  
  187.   
  188. //查询某个兄弟结点-- 横向查询  
  189. CNode*  CNode::QueryBorther(const char* szName)  
  190. {  
  191.     if(szName)  
  192.     {  
  193.         if(m_pFirstBorther)  
  194.         {  
  195.             if(0 == strcmp(szName,m_pFirstBorther->GetName().c_str()))  
  196.             {  
  197.                 return m_pFirstBorther;  
  198.             }  
  199.             else  
  200.             {  
  201.                 //如果不是,查询子结点的子结点。  
  202.                 CNode*  tpChildChild = m_pFirstBorther->QueryChild(szName);  
  203.                 if(tpChildChild)  
  204.                 {  
  205.                     return tpChildChild ;  
  206.                 }  
  207.                 return  m_pFirstBorther->QueryBorther(szName);  
  208.             }  
  209.         }  
  210.     }  
  211.     return NULL;  
  212. }  
  213.   
  214.   
  215. //设置父结点  
  216. void    CNode::SetParent(CNode* pParentNode)  
  217. {  
  218.     m_pParentNode = pParentNode ;  
  219. }  
  220.   
  221. //取得父结点  
  222. CNode*  CNode::GetParent()  
  223. {  
  224.     return m_pParentNode ;  
  225. }  
  226.   
  227. //保存结点树到XML  
  228. bool    CNode::SaveNodeToXML(const char* szXMLFile)  
  229. {  
  230.     FILE*       hFile = fopen(szXMLFile,"wt");  
  231.     if(!hFile)  
  232.     {  
  233.         return false;  
  234.     }  
  235.     fprintf(hFile,TEXT("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"));  
  236.     fprintf(hFile,TEXT("<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"));  
  237.     fprintf(hFile,TEXT("<!--Honghaier Game Tutorial -->\n"));  
  238.       
  239.     fprintf(hFile,TEXT("<plist version=\"1.0\">\n"));  
  240.     fprintf(hFile,TEXT("<dict>\n"));  
  241.     //========================================================  
  242.     fprintf(hFile,TEXT("<key>NodeTree</key>"));  
  243.     fprintf(hFile,TEXT("<dict>"));  
  244.     if(false == SaveNodeToXML(hFile))  
  245.     {     
  246.         fclose(hFile);  
  247.         return false;  
  248.     }  
  249.   
  250.     fprintf(hFile,TEXT("</dict>"));  
  251.     //========================================================  
  252.     fprintf(hFile,TEXT("</dict>"));  
  253.     fprintf(hFile,TEXT("</plist>\n"));  
  254.     fclose(hFile);  
  255.     return true;  
  256. }  
  257. //保存结点树到XML  
  258. bool    CNode::SaveNodeToXML(FILE*  hFile)  
  259. {  
  260.     //========================================================  
  261.     //fprintf(hFile,TEXT("<key>NodeName</key>"));  
  262.     //fprintf(hFile,TEXT("<string>%s</string>"),m_strNodeName.c_str());  
  263.     fprintf(hFile,TEXT("<key>%s</key>"),m_strNodeName.c_str());  
  264.     //========================================================  
  265.     fprintf(hFile,TEXT("<dict>"));  
  266.     if(m_pFirstChild)  
  267.     {  
  268.         if(false == m_pFirstChild->SaveNodeToXML(hFile))  
  269.         {     
  270.             fclose(hFile);  
  271.             return false;  
  272.         }  
  273.     }  
  274.     fprintf(hFile,TEXT("</dict>"));  
  275.   
  276.     if(m_pFirstBorther)  
  277.     {  
  278.         if(false == m_pFirstBorther->SaveNodeToXML(hFile))  
  279.         {     
  280.             fclose(hFile);  
  281.             return false;  
  282.         }  
  283.     }  
  284.     return true;  
  285. }  

         这样,一个最基本的结点就建立起来了,我们将可以由它来建立一棵树,比如下图这样一个程序:我们有一个TreeCtrl。初始情况下只有一个Root结点,通过在树控件上右键弹出菜单中进行选项操作来增加或删除子结点和兄弟结点。当我们创建了一个结点树后可以调用SaveNodeToXML函数来讲结点树保存下来。


保存的XML文件打开后:

、

        学到这里,您已经掌握了一个结点系统的基本设计思想,它将在日后成为一个强大的武器来帮助您在游戏开发过程中解决一些相关的设计问题。

2.1.1结点的位置:

上面的结点系统代码中,只有结点的父子关系,并不能实现父结点移动同时带动子结点移动。这又是怎么做到的呢?

这里有一个关键的核心算法:即一个结点的位置,由本结点相对于父结点位置加上父结点的世界位置来取得,而父结点又会通过父结点与其父结点(即爷爷结点)的相对位置加上其父结点(即爷爷结点)的世界位置来取得。这里有一个层层回溯的思想在里面。

我们在代码中加入一个表示空间位置的结构。

[cpp]  view plain copy
  1. //向量  
  2. struct stVec3  
  3. {  
  4.     //三向值  
  5.     float m_fX;  
  6.     float m_fY;  
  7.     float m_fZ;  
  8.       
  9.     stVec3()  
  10.     {  
  11.         m_fX = 0;  
  12.         m_fY = 0;  
  13.         m_fZ = 0;  
  14.     }  
  15.     //构造  
  16.     stVec3(float x,float y,float z)  
  17.     {  
  18.         m_fX = x;  
  19.         m_fY = y;  
  20.         m_fZ = z;  
  21.     }  
  22.   
  23.     //重载赋值操作符  
  24.     stVec3& stVec3::operator = (const stVec3& tVec3)   
  25.     {  
  26.         m_fX = tVec3.m_fX;  
  27.         m_fY = tVec3.m_fY;  
  28.         m_fZ = tVec3.m_fZ;  
  29.         return *this;  
  30.     }  
  31.     //重载加法操作符  
  32.     stVec3 stVec3::operator + (const stVec3& tVec3)   
  33.     {  
  34.         stVec3  tResultVec;  
  35.         tResultVec.m_fX = m_fX + tVec3.m_fX;  
  36.         tResultVec.m_fY = m_fY + tVec3.m_fY;  
  37.         tResultVec.m_fZ = m_fZ + tVec3.m_fZ;  
  38.         return tResultVec;  
  39.     }  
  40.     //重载加等操作符  
  41.     stVec3& stVec3::operator += (const stVec3& tVec3)   
  42.     {  
  43.         m_fX += tVec3.m_fX;  
  44.         m_fY += tVec3.m_fY;  
  45.         m_fZ += tVec3.m_fZ;  
  46.         return *this;  
  47.     }  
  48.     //重载减法操作符  
  49.     stVec3 stVec3::operator - (const stVec3& tVec3)   
  50.     {  
  51.         stVec3  tResultVec;  
  52.         tResultVec.m_fX = m_fX - tVec3.m_fX;  
  53.         tResultVec.m_fY = m_fY - tVec3.m_fY;  
  54.         tResultVec.m_fZ = m_fZ - tVec3.m_fZ;  
  55.         return tResultVec;  
  56.     }  
  57.     //重载减等操作符  
  58.     stVec3& stVec3::operator -= (const stVec3& tVec3)   
  59.     {  
  60.         m_fX -= tVec3.m_fX;  
  61.         m_fY -= tVec3.m_fY;  
  62.         m_fZ -= tVec3.m_fZ;  
  63.         return *this;  
  64.     }  
  65. }  
  66. ;  
  67.     然后在结点中加入相应接口:  
  68.   
  69. public:  
  70.   
  71.     //设置当前结点相对于父结点位置  
  72.     void    SetPos(float x,float y,float z);  
  73.     void    SetPos_X(float x);  
  74.     void    SetPos_Y(float y);  
  75.     void    SetPos_Z(float z);  
  76.   
  77.     //取得当前结点相对于父结点位置  
  78.     float   GetPos_X();  
  79.     float   GetPos_Y();  
  80.     float   GetPos_Z();  
  81.   
  82.     //取得当前结点的世界坐标位置  
  83.     stVec3  GetWorldPos();  
  84. private:  
  85.     //当前结点相对于父结点的位置  
  86.     stVec3  m_sPos;  
  87.   
  88. 对应的实现:  
  89.   
  90. //设置当前结点相对于父结点位置  
  91. void    CNode::SetPos(float x,float y,float z)  
  92. {  
  93.     m_sPos.m_fX = x;  
  94.     m_sPos.m_fY = y;  
  95.     m_sPos.m_fZ = z;  
  96. }  
  97.   
  98. void    CNode::SetPos_X(float x)  
  99. {  
  100.     m_sPos.m_fX = x;  
  101. }  
  102. void    CNode::SetPos_Y(float y)  
  103. {  
  104.     m_sPos.m_fY = y;  
  105. }  
  106. void    CNode::SetPos_Z(float z)  
  107. {  
  108.     m_sPos.m_fZ = z;  
  109. }  
  110.   
  111. //取得当前结点相对于父结点位置  
  112. float   CNode::GetPos_X()  
  113. {  
  114.     return m_sPos.m_fX;  
  115. }  
  116.   
  117. float   CNode::GetPos_Y()  
  118. {  
  119.     return m_sPos.m_fY;  
  120. }  
  121.   
  122. float   CNode::GetPos_Z()  
  123. {  
  124.     return m_sPos.m_fZ;  
  125. }  
  126.   
  127. //取得当前结点的世界坐标位置  
  128. stVec3  CNode::GetWorldPos()  
  129. {  
  130.     stVec3 tResultPos = m_sPos;  
  131.     //使用回溯法取得最终的世界位置,这一步是结点系统中父结点固定子结点的关健。  
  132.     if(m_pParentNode)  
  133.     {  
  134.         tResultPos += m_pParentNode->GetWorldPos();  
  135.     }  
  136.     return tResultPos;  
  137. }  

         经过这些代码的建立,我们就可以取得一个受父结点位置固定的子结点的世界位置了。同样,缩放和旋转的关系也可以由此建立,在此就不一一赘述了,有兴趣的同学可以在本节作用中完成它。

2.2 精灵,层,场景

2.2.1魂斗罗的场景:

 

         在Cocos2d-x中,大量的物体都是基于结点系统的,这些类均是由最基本的结点类CCNode来派生的。其中最为重要的是精灵-CCSprite,层-CCLayer,场景- CCScene。

         一个游戏的一个关卡,可以想象为一棵树,其中场景是树干,层是树枝,精灵是树叶。一棵树只有一个树干,树干上有多个树枝,树枝上又有多个树叶。从功能性上来讲,树干的作用是管理树枝,树枝的作用是固定其上长出的树叶,树叶的作用是吸收阳光…NO,不是这个意思,树叶的作用是表现力,是观赏,是用来看的。表现在Cocos2d-x的游戏中,场景用来管理所有的层,而层则是用来区分具有不同排序分类的精灵集合,并能响应触屏事件,而精灵就是显示图片和动画的。当游戏要切换场景时,就像是换一棵树。作为一个游戏设计师,要能够很好的设计游戏的这些树。当然,我们要很清楚的知道如何来种下一棵树,这很重要!

         首先,我们先要确定游戏都需要哪些场景。作为树的根结点,它构成了游戏的骨架。比如我们小时候玩的FC游戏-《魂斗罗》。

我们可以将开始界面和后面每一个关卡都当作是一个场景。那简单来说这个游戏是由两类场景构成的。第一类场景就是开始界面,如下图:


 

         这个开始界面做为一个场景是简单了点,但很清晰。游戏开始时首先要运行的场景就是它。我们以三级树形结点来表示这个场景。




         在这个三级树形结点图示中,“开始界面”即为场景,“界面层”即为层,再下面的四个结点可以算为界面层下的精灵,当然,菜单其实也可以分为几个精灵构成。

         第二类场景就是关卡了。如图:



         这是熟悉的第一关,我们仍以三级树形结点来表示这个场景。


         在这里,“第一关”即为场景,为了区分具有不同排序分类的精灵集合。我将游戏中的层由远及近观看,由先及后绘制,划分为“远景层”,“近景层”,“人物层”,“效果层”,“界面层”等五个层,再将各种精灵分布到这些层中。

         继续这样子分析,我们可以得出所有的关卡树:



         在这里“Root”代表了游戏程序。它共种有十棵树。分别为“开始界面”,“第一关”…“通关界面”,每完成一个关卡,就将进行场景的切换,也就是显示一棵新树。

        

         到这里,精灵,层与场景的结点关系原理已经讲解完成。我们现在来看一下Cocos2d-x中是如何具体实现和应用的。

        

         以开始界面为例,咱们接着上一节中所讲的节点类来进行扩展,为了更好的讲述理论,这部分内容完全不涉及任何渲染引擎的使用,我们只使用VS创建一个简单的WIN32窗口程序,并使用GDI来进行绘制。

        

         我们将创建的工程命名为ShowNodeTree,编译运行只有一个空白窗口,它工作的很好。OK,现在我们创建一个工程筛选目录NodoTree,并将之前创建的Node放入其中,并依次创建好Scene,Layer,Spriet及Director等类。



 

顾名思义,上面这些文件分别为:

 

         Director.h/cpp:win32绘制管理类CDirector,绘图用。

         Node.h/cpp:结点基类CNode,用于构建结点树。

         Layer.h/cpp: 层类CLayer。

         Scene.h/cpp:场景类CScene。

         Sprite.h/cpp:精灵类CSprite。

 

我们来看一下具体实现:

首先是win32绘制管理类CDirector:

Director.h:

[cpp]  view plain copy
  1. #pragma once  
  2. #include <windows.h>  
  3. //==================================================================  
  4. //File:Director.h  
  5. //Desc:显示设备类,用于绘制  
  6. //==================================================================  
  7. class   CDirector  
  8. {  
  9. public:  
  10.     ~CDirector();  
  11. public:  
  12.     //获取单件实例指针  
  13.     static  CDirector* GetInstance();  
  14.     //设置HDC  
  15.     void    Init(HWND hWnd);  
  16.     //绘制矩形  
  17.     void    FillRect(int x,int y,int width,int height,COLORREF  rgb);  
  18.     //绘制图像  
  19.     void    DrawBitMap(int x,int y,int width,int height,HBITMAP hBitmap);  
  20. private:  
  21.     CDirector(){}  
  22. private:  
  23.     //单件实例指针  
  24.     static  CDirector*  m_pThisInst;  
  25.     //WINDOWS 窗口句柄  
  26.     HWND        m_HWnd;  
  27.     //WINDOWS GDI 绘图所用的设备上下文  
  28.     HDC         m_HDC;  
  29.   
  30. }  
  31. ;  

 

可以看到,CDirector类是一个单例,我们为其创建了两个函数来进行绘制指定色的矩形和绘制位图的功能。没错,足够用了。

Director.cpp:

[cpp]  view plain copy
  1. #include "Director.h"  
  2. CDirector*  CDirector::m_pThisInst = NULL;  
  3.   
  4. CDirector* CDirector::GetInstance()  
  5. {  
  6.     if(!m_pThisInst)  
  7.     {  
  8.         m_pThisInst = new CDirector ;  
  9.         if(!m_pThisInst)return NULL;  
  10.         m_pThisInst->Init(NULL);  
  11.     }  
  12.     return m_pThisInst;  
  13. }  
  14.   
  15. CDirector::~CDirector()  
  16. {  
  17.     if(m_pThisInst)  
  18.     {  
  19.         delete m_pThisInst;  
  20.         m_pThisInst = NULL;  
  21.     }  
  22. }  
  23. void    CDirector::Init(HWND hWnd)  
  24. {  
  25.     if(hWnd)  
  26.     {  
  27.         m_HWnd = hWnd ;  
  28.         m_HDC = ::GetDC(m_HWnd) ;  
  29.     }  
  30. }  
  31. void    CDirector::FillRect(int x,int y,int width,int height,COLORREF   rgb)  
  32. {  
  33.     HBRUSH hBrush = ::CreateSolidBrush(rgb);  
  34.     RECT   tRect;  
  35.     tRect.left      = x;  
  36.     tRect.top       = y;  
  37.     tRect.right     = x + width;  
  38.     tRect.bottom    = y + height;  
  39.     ::FillRect(m_HDC,&tRect,hBrush);  
  40.     ::DeleteObject(hBrush);  
  41. }  
  42.   
  43. void    CDirector::DrawBitMap(int x,int y,int width,int height,HBITMAP  hBitmap)  
  44. {  
  45.   
  46.     HDC hTempHDC = CreateCompatibleDC(m_HDC);  
  47.     HGDIOBJ hOldObj = SelectObject(hTempHDC,hBitmap);  
  48.     BitBlt(m_HDC,x,y,width, height,hTempHDC,0,0,SRCCOPY);         
  49.     DeleteDC(hTempHDC);  
  50. }  

 

         都是最基本的GDI绘制操作,这样我们的设备就建立好了。下面我们来创建场景。

Scene.h:

[cpp]  view plain copy
  1. #pragma once  
  2. #include "Node.h"  
  3. //==================================================================  
  4. //File:Scene.h  
  5. //Desc:场景类,用于管理所有的层  
  6. //==================================================================  
  7. //结点类  
  8. class CScene : public CNode  
  9. {  
  10. public:  
  11.     //构造  
  12.     CScene(const char* szName);  
  13. };  

其对应的CPP:

[cpp]  view plain copy
  1. #include "Scene.h"  
  2. //构造  
  3. CScene::CScene(const char* szName)  
  4. {  
  5.     SetName(szName);  
  6. }  

         没什么可解释的,就是一个结点类。然后是层:

Layer.h:

[cpp]  view plain copy
  1. #pragma once  
  2. #include "Node.h"  
  3. //==================================================================  
  4. //File:Layer.h  
  5. //Desc:层类,用于存放精灵  
  6. //==================================================================  
  7. //结点类  
  8. class CLayer : public CNode  
  9. {  
  10. public:  
  11.     //构造  
  12.     CLayer(const char* szName);  
  13. public:  
  14.     //更新  
  15.     virtual     inline  void    Update();  
  16.     //直接渲染  
  17.     virtual     inline  void    Draw();  
  18. public:  
  19.     //设置颜色  
  20.     void        SetColor(COLORREF color);  
  21.     //取得颜色  
  22.     COLORREF    GetColor();  
  23.     //设置高  
  24.     void        SetWidth(int vWidth);  
  25.     //取得宽  
  26.     int         GetWidth();  
  27.     //设置高  
  28.     void        SetHeight(int vHeight);  
  29.     //取得高  
  30.     int         GetHeight();  
  31. private:  
  32.     //设置颜色  
  33.     COLORREF    m_LayerColor;  
  34.     //宽度  
  35.     int         m_nWidth;  
  36.     //高度  
  37.     int         m_nHeight;  
  38. };  

可以看到,层有了宽高和颜色的设置,对应的Layer.cpp:

[cpp]  view plain copy
  1. #include "Layer.h"  
  2. #include "Director.h"  
  3. //构造  
  4. CLayer::CLayer(const char* szName):  
  5. m_nWidth(0),  
  6. m_nHeight(0)  
  7. {  
  8.     SetName(szName);  
  9.     m_LayerColor = RGB(255,255,255);  
  10. }  
  11. //更新  
  12. void    CLayer::Update()  
  13. {  
  14.     CNode::Update();  
  15. }  
  16. //直接渲染  
  17. void    CLayer::Draw()  
  18. {  
  19.     stVec3  tPos = GetWorldPos();  
  20.     CDirector::GetInstance()->FillRect(tPos.m_fX,tPos.m_fY,m_nWidth,m_nHeight,m_LayerColor);  
  21.     CNode::Draw();  
  22. }  
  23. //设置颜色  
  24. void    CLayer::SetColor(COLORREF color)  
  25. {  
  26.     m_LayerColor = color;  
  27. }  
  28. //取得颜色  
  29. COLORREF  CLayer::GetColor()  
  30. {  
  31.     return m_LayerColor ;  
  32. }  
  33. //设置宽  
  34. void    CLayer::SetWidth(int vWidth)  
  35. {  
  36.     m_nWidth = vWidth;  
  37. }  
  38. //取得宽  
  39. int     CLayer::GetWidth()  
  40. {  
  41.     return m_nWidth ;  
  42. }  
  43. //设置高  
  44. void    CLayer::SetHeight(int vHeight)  
  45. {  
  46.     m_nHeight = vHeight;  
  47. }  
  48. //取得高  
  49. int     CLayer::GetHeight()  
  50. {  
  51.     return m_nHeight ;  
  52. }  


         层已经可以显示了,通过取得设备并调用FillRect来显示一个色块矩形。最后我们来看一下精灵:

Sprite.h:

[cpp]  view plain copy
  1. #pragma once  
  2. #include "Node.h"  
  3. //==================================================================  
  4. //File:Sprite.h  
  5. //Desc:精灵类,用于显示图片  
  6. //==================================================================  
  7. //结点类  
  8. class CSprite : public CNode  
  9. {  
  10. public:  
  11.     //构造  
  12.     CSprite(const char* szName);  
  13.   
  14. public:  
  15.     //更新  
  16.     virtual  inline void    Update();  
  17.     //直接渲染  
  18.     virtual  inline void    Draw();  
  19. public:  
  20.     //设置位图句柄  
  21.     void    SetBitmap(HBITMAP vhBitmap);  
  22.     //设置位图句柄  
  23.     void    SetBitmap(HBITMAP vhBitmap,int vWidth,int vHeight);  
  24. private:  
  25.     //所用位图句柄  
  26.     HBITMAP m_hBitmap;  
  27.     //位图宽度  
  28.     int     m_nBitmapWidth;  
  29.     //位图高度  
  30.     int     m_nBitmapHeight;  
  31. };  

         我们为精灵增加了位图句柄,以使它可以绘制相应的位图。

Sprite.cpp:

[cpp]  view plain copy
  1. #include "Sprite.h"  
  2. #include "Director.h"  
  3. //构造  
  4. CSprite::CSprite(const char* szName):  
  5. m_hBitmap(NULL),  
  6. m_nBitmapWidth(0),  
  7. m_nBitmapHeight(0)  
  8. {  
  9.     SetName(szName);  
  10. }  
  11.   
  12. //更新  
  13. void    CSprite::Update()  
  14. {  
  15.     CNode::Update();  
  16. }  
  17. //直接渲染  
  18. void    CSprite::Draw()  
  19. {  
  20.     if(m_hBitmap)  
  21.     {  
  22.         stVec3  tPos = GetWorldPos();  
  23.         CDirector::GetInstance()->DrawBitMap(tPos.m_fX,tPos.m_fY,m_nBitmapWidth,m_nBitmapHeight,m_hBitmap);  
  24.     }  
  25.     CNode::Draw();  
  26. }  
  27. //设置位图句柄  
  28. void    CSprite::SetBitmap(HBITMAP vhBitmap)  
  29. {  
  30.     BITMAP bmp ;  
  31.     GetObject(vhBitmap, sizeof(BITMAP), &bmp);  //得到一个位图对象   
  32.     m_hBitmap = vhBitmap ;  
  33.     m_nBitmapWidth = bmp.bmWidth ;  
  34.     m_nBitmapHeight = bmp.bmHeight ;  
  35. }  
  36. //设置位图句柄  
  37. void    CSprite::SetBitmap(HBITMAP vhBitmap,int vWidth,int vHeight)  
  38. {  
  39.     m_hBitmap = vhBitmap ;  
  40.     m_nBitmapWidth = vWidth ;  
  41.     m_nBitmapHeight = vHeight;  
  42. }  

 

         OK,就这样,我们就建立了一套可以进行场景,层,精灵管理和绘制的类。现在我们来具体的实现一下开始界面。我将开始界面分为



 

         这里共有一个层和八个精灵。层嘛,就是一纯黑背景色块,八个精灵嘛,就如上图所示分别用来显示不同的位图:




 我们现在打开程序的主源文件ShowNodeTree.cpp,在文件顶部加入:

[cpp]  view plain copy
  1. #include "Sprite.h"  
  2. #include "Layer.h"  
  3. #include "Scene.h"  
  4. #include "Director.h"  
  5. //唯一使用的场景  
  6. CScene*  g_pMyScene = NULL;  

并在InitInstance函数的尾部加入:

[cpp]  view plain copy
  1. //初始化设备  
  2.    CDirector::GetInstance()->Init(hWnd);  
  3.    //增加层  
  4.    CLayer* pNewLayer = new CLayer("Layer1");  
  5.    pNewLayer->SetPos(100,40,0);  
  6.    pNewLayer->SetColor(RGB(0,0,0));  
  7.    pNewLayer->SetWidth(497);  
  8.    pNewLayer->SetHeight(434);  
  9.   
  10.    //增加精灵  
  11.    char  szCurrDir[_MAX_PATH];  
  12.    ::GetCurrentDirectory(_MAX_PATH,szCurrDir);  
  13.    char  szImagePathName[_MAX_PATH];  
  14.    wsprintf(szImagePathName,"%s\\knm.bmp",szCurrDir);  
  15.    HBITMAP  hbmp = (HBITMAP)LoadImage(NULL,szImagePathName,IMAGE_BITMAP,0,0,LR_LOADFROMFILE|LR_CREATEDIBSECTION);  
  16.    CSprite* pNewSprite = new CSprite("knm");  
  17.    pNewSprite->SetBitmap(hbmp);  
  18.    pNewSprite->SetPos(130,40,0);  
  19.    //将精灵放入到层  
  20.    pNewLayer->AddChild(pNewSprite);  
  21.   
  22.    wsprintf(szImagePathName,"%s\\logo.bmp",szCurrDir);  
  23.    hbmp = (HBITMAP)LoadImage(NULL,szImagePathName,IMAGE_BITMAP,0,0,LR_LOADFROMFILE|LR_CREATEDIBSECTION);  
  24.    CSprite* pNewSprite2 = new CSprite("logo");  
  25.    pNewSprite2->SetBitmap(hbmp);  
  26.    pNewSprite2->SetPos(90,100,0);  
  27.    //将精灵放入到层  
  28.    pNewLayer->AddChild(pNewSprite2);  
  29.   
  30.    wsprintf(szImagePathName,"%s\\player.bmp",szCurrDir);  
  31.    hbmp = (HBITMAP)LoadImage(NULL,szImagePathName,IMAGE_BITMAP,0,0,LR_LOADFROMFILE|LR_CREATEDIBSECTION);  
  32.    CSprite* pNewSprite3 = new CSprite("player");  
  33.    pNewSprite3->SetBitmap(hbmp);  
  34.    pNewSprite3->SetPos(260,230,0);  
  35.    //将精灵放入到层  
  36.    pNewLayer->AddChild(pNewSprite3);  
  37.   
  38.   
  39.    wsprintf(szImagePathName,"%s\\menu_title.bmp",szCurrDir);  
  40.    hbmp = (HBITMAP)LoadImage(NULL,szImagePathName,IMAGE_BITMAP,0,0,LR_LOADFROMFILE|LR_CREATEDIBSECTION);  
  41.    CSprite* pNewSprite4 = new CSprite("menu_title");  
  42.    pNewSprite4->SetBitmap(hbmp);  
  43.    pNewSprite4->SetPos(40,270,0);  
  44.    //将精灵放入到层  
  45.    pNewLayer->AddChild(pNewSprite4);  
  46.   
  47.    wsprintf(szImagePathName,"%s\\menu_1.bmp",szCurrDir);  
  48.    hbmp = (HBITMAP)LoadImage(NULL,szImagePathName,IMAGE_BITMAP,0,0,LR_LOADFROMFILE|LR_CREATEDIBSECTION);  
  49.    CSprite* pNewSprite5 = new CSprite("menu_1");  
  50.    pNewSprite5->SetBitmap(hbmp);  
  51.    pNewSprite5->SetPos(100,310,0);  
  52.    //将精灵放入到层  
  53.    pNewLayer->AddChild(pNewSprite5);  
  54.   
  55.    wsprintf(szImagePathName,"%s\\menu_2.bmp",szCurrDir);  
  56.    hbmp = (HBITMAP)LoadImage(NULL,szImagePathName,IMAGE_BITMAP,0,0,LR_LOADFROMFILE|LR_CREATEDIBSECTION);  
  57.    CSprite* pNewSprite6 = new CSprite("menu_2");  
  58.    pNewSprite6->SetBitmap(hbmp);  
  59.    pNewSprite6->SetPos(100,350,0);  
  60.    //将精灵放入到层  
  61.    pNewLayer->AddChild(pNewSprite6);  
  62.   
  63.    wsprintf(szImagePathName,"%s\\menu_cursor.bmp",szCurrDir);  
  64.    hbmp = (HBITMAP)LoadImage(NULL,szImagePathName,IMAGE_BITMAP,0,0,LR_LOADFROMFILE|LR_CREATEDIBSECTION);  
  65.    CSprite* pNewSprite7 = new CSprite("menu_cursor");  
  66.    pNewSprite7->SetBitmap(hbmp);  
  67.    pNewSprite7->SetPos(60,310,0);  
  68.    //将精灵放入到层  
  69.    pNewLayer->AddChild(pNewSprite7);  
  70.   
  71.   
  72.    wsprintf(szImagePathName,"%s\\copyright.bmp",szCurrDir);  
  73.    hbmp = (HBITMAP)LoadImage(NULL,szImagePathName,IMAGE_BITMAP,0,0,LR_LOADFROMFILE|LR_CREATEDIBSECTION);  
  74.    CSprite* pNewSprite8 = new CSprite("copyright");  
  75.    pNewSprite8->SetBitmap(hbmp);  
  76.    pNewSprite8->SetPos(120,390,0);  
  77.    //将精灵放入到层  
  78.    pNewLayer->AddChild(pNewSprite8);  
  79.   
  80.    //将层放入场景  
  81.    g_pMyScene = new CScene("HDL");  
  82.    g_pMyScene->AddChild(pNewLayer);  
  83.   
  84.    //设定每毫秒刷新一帧  
  85.    ::SetTimer(hWnd,1,20,NULL);  

 

    看,经过上面的代码之后,我们就创建了相应的层,精灵和场景。最后创建一个定时器来进行屏幕重绘,FPS嘛,就设为50好了。

    我们在窗口消息处理函数中加入:

[cpp]  view plain copy
  1. case WM_PAINT:  
  2.         {  
  3.             hdc = BeginPaint(hWnd, &ps);  
  4.             // TODO: 在此添加任意绘图代码..  
  5.             if(g_pMyScene)  
  6.             {  
  7.                 //更新和绘制场景  
  8.                 g_pMyScene->Update();  
  9.                 g_pMyScene->Draw();  
  10.             }  
  11.             EndPaint(hWnd, &ps);  
  12.         }  
  13.         break;  
  14.     case WM_TIMER:  
  15.         {  
  16.             //让场景的Layer1层不断向右移动,到像素时重置。  
  17.             if(g_pMyScene)  
  18.             {  
  19.                 CNode*  pLayer = g_pMyScene->QueryChild("Layer1");  
  20.                 stVec3  tPos   = pLayer->GetWorldPos();  
  21.                 tPos.m_fX += 1;  
  22.                 if(tPos.m_fX > 400)  
  23.                 {  
  24.                     tPos.m_fX = 0;  
  25.                 }  
  26.                 pLayer->SetPos_X(tPos.m_fX);  
  27.             }  
  28.             //响应刷新  
  29.             ::InvalidateRect(hWnd,NULL,TRUE);  
  30.         }  
  31.         break;  
  32.     case WM_KEYDOWN:  
  33.         {  
  34.             if(wParam == VK_UP)  
  35.             {//按上时选人菜单光标置在第一项前面。  
  36.                 if(g_pMyScene)  
  37.                 {  
  38.                     CSprite*    pNewSprite7 = (CSprite*)g_pMyScene->QueryChild("menu_cursor");  
  39.                     if(pNewSprite7)  
  40.                     {  
  41.                         pNewSprite7->SetPos(60,310,0);  
  42.                     }  
  43.                 }  
  44.             }  
  45.             if(wParam == VK_DOWN)  
  46.             {//按下时选人菜单光标置在第二项前面。  
  47.                 if(g_pMyScene)  
  48.                 {  
  49.                     CSprite*    pNewSprite7 = (CSprite*)g_pMyScene->QueryChild("menu_cursor");  
  50.                     if(pNewSprite7)  
  51.                     {  
  52.                         pNewSprite7->SetPos(60,350,0);  
  53.                     }  
  54.                 }  
  55.             }  
  56.         }  
  57.         break;  
  58.     case WM_DESTROY:  
  59.         //当窗口销毁时也一并删除定时器并释放场景。  
  60.         ::KillTimer(hWnd,1);  
  61.         if(g_pMyScene)  
  62.         {  
  63.             //会调用CNode的虚析构函数释放所有子结点。所以不会造成内存泄漏。  
  64.             delete g_pMyScene;  
  65.             g_pMyScene = NULL;  
  66.         }  
  67.         PostQuitMessage(0);  
  68.         break;  

    这样我们的开始界面就算完成了,编译运行一下吧:



        怎么样?不错吧。一个开始界面层展现在我们面前,所有精灵做为层的子结点而随着层保持运动。虽然这种方式还有一些闪屏,但,那并不是重点,关键是你彻彻底底的理解了结点系统对于引擎架构的作用和设计思想。好了,喝口水歇一会儿开始进入到Cocos2d-x中去看看。

2.1.2 Cocos2d-x中的精灵,层,场景与结点:


         在Cocos2d-x中,结点的基类是CCNode,它的实现远远超越了上面结点代码的复杂度,不过没关系,随着后面相关代码接触的加深,你可以很明白它的全部接口函义,但现在,你所需要的只是明白它就不过是个结点,它不过是咱们上面结点类的演变,说的通俗点:不要以为你穿个马甲哥就认不出你了!

 

         在CCNode中,有一个指针容器成员m_pChildren ,它存放了当前结点下的所有子结点,我们通过addChild来增加子结点到其中。我们并没有发现所谓的兄弟结点,为什么呢?那时因为兄弟结点被“扁平化”处理了。为了提升效率,减少递归调用的次数,可以将所有子结点的指针都存放在当前结点的容器中,所以子结点的兄弟结点就不必出现了。

 

         有了结点CCNode,我们来看一下精灵CCSprite,它在libcocos2d的sprite_nodes分类下。

打开CCSprite.h:

 

         CCSprite : publicCCNode,public CCTextureProtocol,public CCRGBAProtocol

        

         很明显,精灵是由结点CCNode派生出来的子类。它的主要功能就是显示图形。在其函数中,涉及纹理加载和OpenGL相关的顶点和颜色,纹理寻址的操作。

        

         层CCLayer和场景CCScene是被存放在libcocos2d的layers_scenes_transitions_nodes分类下。

         打开CCLayer.h:

 

CC_DLLCCLayer : public CCNode,publicCCTouchDelegate,publicCCAccelerometerDelegate,publicCCKeypadDelegate

 

         可以看到,CCLayer除了由结点CCNode派生外,还增加了用户输入事件的响应接口。如CCTouchDelegate是触屏事件响应接口类,CCAccelerometerDelegate是加速键消息事件响应接口类,CCKeypadDelegate是软键盘消息事件响应接口类。

 

    打开CCScene.h:

class CC_DLL CCScene :publicCCNode

         好吧,真是简单明了,场景就是专门管理子结点的,或者说就是专门管理层结点的。

 

         现在我们来看一些它们的具体应用。

         打开HelloCpp工程。在Classes下我们看到有两个类:

 

1 . AppDelegate:由CCApplication派生,即Cocos2d-x的程序类。可以把它当作上面图示中的”Root”。它的作用就是启动一个程序,创建主窗口并初始化游戏引擎并进入消息循环。

 

2 . HelloWorld:由CCLayer派生,即Cocos2d-x的层。对应上面图示中“开始界面”场景中的“界面层”。它的作用是显示背景图和菜单及退出按钮等精灵。在这个类里有一个静态函数HelloWorld::scene()创建了所用到的场景并创建HelloWorld这个层放入到场景中。

 

         在程序的main函数中创建了AppDelegate类的实例对象并调用run运行。          之后会在AppDelegate的函数applicationDidFinishLaunching(代表程序启动时的处理)中结尾处调用HelloWorld::scene()创建了场景。

 

         游戏运行起来是个什么样子呢?没错,我看跟魂斗罗的“开始界面”也差不到哪去嘛。当然,只是指组织关系。



         嗯,到此,本节的知识算是讲述完毕!做为一个有上进心的程序员,咱们来做些课后题吧?

2.3 课后题目

1.在2.1.1结中为结点增加缩放,旋转(当然,如果没有好的数学知识就算了,也可以用一些第三方数学库)的处理,使取得一个子结点在世界坐标系中的大小时会受到其父结点的影响。

 

2.  将《魂斗罗》之外你玩过的游戏选几个做一个树型分析。

     
最后,本节所涉及实例工程代码下载: http://download.youkuaiyun.com/detail/honghaier/5221534

华南理工大学-大学物理实验-不良导体热导率测量 不良导体热导率测量 引言 导热系数(又叫热导率)是反映材料热性能的重要物理量。热传导是热交换的三种(热传导、对流和辐射)基本形式之一,是工程热物理、材料科学、固体物理及能源、环保等各个研究领域的课题。材料的导热机理在很大程度上取决于它的微观结构,热量的传递依靠原子、分子围绕平衡位置的振动以及自由电子的迁移。在金属中电子流起支配作用,在绝缘体和大部分半导体中则以晶格振动起主导作用。因此,某种材料的导热系数不仅与构成材料的物质种类密切相关,而且还与材料的微观结构、温度、压力及杂质含量相联系。在科学实验和工程设计 阅读详情

相关推荐

pytorch中dataloader的num_workers参数

结论速递 在Windows系统中,num_workers参数建议设为0,在Linux系统则不需担心。 1 问题描述 在之前的任务超大图上的节点表征学习中,使用PyG库用于数据加载的DataLoader,ClusterLoader等类时,会涉及到参数num_workers。在不同系统环境试验该参数的设定值时,会出现不同的结果 colab(Linux),设为12 有warning message,提示num_workers参数设置不合理,不影响继续运行 /usr/local/lib/python3.7/di

qq_40990057的博客 1万+

礼物:《红孩儿引擎内功心法修练与Cocos2d-x》之结点系统(场景,层,精灵)

[Cocos2d-x相关教程来源于红孩儿的游戏编程之路优快云博客地址:http://blog.youkuaiyun.com/honghaier 红孩儿Cocos2d-X学习园地QQ3群:205100149,47870848 礼物:《红孩儿引擎内功心法修练与Cocos2d-x》之结点系统(场景,层,精灵)          另:好吧,我彻底被Cocos2d-x版本的更新速度打败了!我

红孩儿的游戏编程之路 1万+

STM32F103ZE精英板驱动ESP8266与DHT11构建物联网网关,实现乐联网温湿度数据实时监控

本文详细介绍了如何使用STM32F103ZE精英板驱动ESP8266与DHT11构建物联网网关,实现乐联网温湿度数据实时监控。从硬件选型、连接调试到乐联网平台配置和STM32程序开发,提供了完整的实现步骤和常见问题解决方案,适合物联网入门开发者参考。

weixin_30823001的博客 78

cocosCreator笔记 之 组件脚本

cocosCreator 之 组件脚本和生命周期说明

鹤九日的博客 2065

Ubuntu系统 nano 编辑器使用教程

• 原因:文件编码不兼容(如 Windows 的 CRLF 换行符)。• 若使用 SSH 工具(如 PuTTY),确保终端设置中未占用。掌握这些操作后,你可以高效使用 nano 完成大部分文本编辑任务!• 底部快捷键栏:显示常用操作的快捷键(• 如果文件已修改,会提示是否保存:按。• 顶部状态栏:显示文件名和编辑状态。表示 Ctrl + G(查看帮助)。保存时自动生成备份文件(原文件名后加。• 强制保存只读文件:用。启动 nano 时添加。启动 nano 时添加。(重做)触发自动缩进。

qq_44534541的博客 2083

mot_challenge官方评估代码

mot_challenge官方代码使用介绍 评估mot17验证集:每个视频序列的后一半帧作为验证集 下载源码:git clone https://hub.fastgit.org/JonathonLuiten/TrackEval.git 官网上下载data样例,放在TrackEval/下,结构如下: data |-- gt | |-- bdd100k | |-- davis | |-- kitti | |-- mot_challenge | | |-- MOT15-train |

qq_37866436的博客 4894

cocos获取节点

/在子物体获取组件。

weixin_45514287的博客 437

CloudCompare里那个CSF地面滤波插件,到底怎么用?手把手教你分离点云里的地面

本文详细介绍了如何在CloudCompare中使用CSF地面滤波插件分离点云中的地面数据。通过解析布料模拟滤波算法的核心参数,如布料刚度和网格分辨率,并提供不同地形(城市、丘陵、山地)的适配策略,帮助用户高效处理复杂点云数据。文章还包含实战案例和优化技巧,确保滤波结果的精度和可靠性。

weixin_42548708的博客 221

CocosCreator开发系列教程(三):Cocos Creator场景树与节点

本章讲解了Cocos Creator场景树与节点的相关知识

qq_42182650的博客 2219

ORB-SLAM3代码详解 - 第 18 篇 · Atlas 多地图系统、地图保存与全系统调优

会跑:理解三线程架构与数据流(01/08/10/13),能编译、配参、复现各数据集结果。会改:掌握相机模型抽象(05)、g2o 顶点与边(14)、预积分(15),能接入新传感器、新相机、新约束。会调:理解每个参数背后的物理含义与耦合(03/15/16/18),能针对自己的平台与场景做精度/实时/鲁棒的权衡。ORB-SLAM3 的价值不止于一个可用的 SLAM 系统,更在于它是一套工程化的几何状态估计模板:清晰的模块边界、可替换的相机/传感器抽象、紧耦合优化的标准范式。

PX4-SLAM-PNC 171

matlab 点云均值、中值、高斯滤波【2024最新版】

点云均值滤波、中值滤波、高斯滤波的matlab自带函数实现。博客长期更新,本文最近一次更新时间为:2024年11月9日。

点云侠的博客 5259

Cocos Creator在TypeScript中获取其他节点及组件

在 Cocos Creator 中,要获取其他节点,可以使用 cc.find() 函数或 node.getChildByName() 方法。

m0_66016308的博客 3954

第14.5章 高翔博士最新开源框架Lightning-LM源码解读(五):揭秘位姿图优化核心模块,从数学原理到代码实现

本文介绍了Lightning-LM框架中的位姿图优化(PGO)模块实现。该模块通过融合多源定位信息(激光定位、激光里程计、DR等),构建并优化位姿图来获得更精确的位姿估计。 主要内容包括: 模块概况与数据流程:初始化配置→添加PGO帧→分配位姿→优化求解→更新位姿→滑动窗口 16个核心函数详解:从构造函数到边缘化处理,涵盖完整的PGO工作流程 关键技术点:噪声参数设置、优化问题构建、约束添加、异常值处理等 关键数据结构:帧队列管理、顶点和边构建、优化统计信息收集 该PGO模块采用增量式优化方式,支持多种约束

行知SLAM 1216

《红孩儿引擎内功心法修练与Cocos2d-x》之结点系统(场景,层,精灵)

[Cocos2d-x相关教程来源于红孩儿的游戏编程之路优快云博客地址:http://blog.youkuaiyun.com/honghaier 礼物:《红孩儿引擎内功心法修练与Cocos2d-x》之结点系统(场景,层,精灵)          另:好吧,我彻底被Cocos2d-x版本的更新速度打败了!我决定停止再对2.0.2版本再做深入分析讲解。本博文实为新书的节选,做为礼物送给大家

huadou1125的专栏 1292

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

[置顶] 礼物:《红孩儿引擎内功心法修练与Cocos2d-x》之结点系统(场景,层,精灵)...

  [Cocos2d-x相关教程来源于红孩儿的游戏编程之路优快云博客地址:http://blog.youkuaiyun.com/honghaier 红孩儿Cocos2d-X学习园地QQ3群:205100149,47870848   礼物:《红孩儿引擎内功心法修练与Cocos2d-x》之结点系统(场景,层,精灵)           另:好吧,我彻底被Cocos2d-x版...

weixin_33853827的博客 126

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

【python】python小游戏——开心消消乐(源码)【独一无二】

👉博__主👈:米码收割机👉技__能👈:C++/Python语言👉公众号👈:测试开发自动化【获取源码+商业合作】👉荣__誉👈:阿里云博客专家博主、51CTO技术博主👉专__注👈:专注主流机器人、人工智能等相关领域的开发、测试技术。

测试开发自动化 5758

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

如何选择元素的前一个兄弟元素

在 CSS 中,没有一种方法可以选择前一个兄弟元素。因为 CSS 是从上到下进行渲染的,所以不可能知道一个元素在其之前出现的元素。/* 选择所有 class=“box” 的元素,且它们后面紧跟的是 class=“highlight” 的元素 */如果你需要在 JavaScript 中选择前一个兄弟元素,你可以使用。然而,如果你想要选择后一个兄弟元素,你可以使用。// 对前一个兄弟元素做些什么。如何选择元素的前一个兄弟元素。

qq_18422039的博客 3407

红孩儿网狐Cocos经典棋牌开发教程

由知名Cocos技术讲师红孩儿讲解的基于网狐源码进行棋牌开发的系列教程。

Cocos Creator 获取节点的方式

1.通过find方式获取//获取节点 var node=cc.find("Canvas/logo"); //获取精灵组件 var sprite=node.getComponent(cc.Sprite);2.通过在构造函数中定义组件,并且从层级管理器中拉入节点。 cc.Class({ extends: cc.Component, properties: { label...

weixin_33873846的博客 6194

Cocos Cerator调整节点顺序后获取节点位置信息

    阅读前,建议学习下皮佬的文章 — Cocos Creator 源码解读:siblingIndex 与 zIndex 解决     通过setSiblingIndex改变节点在父节点中的顺序,即node.parent.chindren[index],想拿到某个节点排序后的坐标,监听事件就行了。注意: 按你需求on+ off监听或once监听 // 当节点在兄弟节点中的顺序发生变化时触发的事件。 cc.Node.EventType.SIBLING_ORDER_CHANGED node.on(c

qq_43811107的博客 2319

Cocos Creator学习二:查找节点和查找组件

1.目的:只有通过方便的获取节点对象以及组件,才能较好的进行逻辑控制。 2.通过 cc.find(节点全路径名称字符串) 获取节点。 3.通过getComponent获取组件(注意一个是类型,一个是类名)。 ①通过 cc.Node.getComponent(类型) 获取节点中该类型对应的组件。 ②通过 cc.Node.getComponent(类名) 获取节点中该类名称对应的组...

weixin_33895604的博客 2745

视频教程-使用Cocos2d-x 开发3D游戏-手游开发

使用Cocos2d-x 开发3D游戏 中国早期游戏程序员,2003年起从事游...

weixin_28957657的博客 433

JS获取子节点、父节点和兄弟节点的若干种方式

一、js获取子节点的方式1.通过获取dom方式直接获取子节点其中test的父标签id的值,div为标签的名字。getElementsByTagName是一个方法。返回的是一个数组。在访问的时候要按数组的形式访问。var a = document.getElementById("test").getElementsByTagName("div"); 2.通过childNodes获取子节点使用child

laok_的博客 29万+

CocosCreator获取某个节点

//getCholdByName 获取某个节点     getComponent  获取节点的某个元素             that._addSpriteFrameToContainer(newNode.getChildByName('pic').getComponent(cc.Sprite), personOb.icon)

xinsong520的博客 1万+

cocos2dx 3.3 魂斗罗初步尝试 (目前)敌人和子弹(只做了一点点)

想把敌人设计为一个类,gen'ju

fztfztfzt的专栏 1997
上一篇: C++对象的内存分析(精)
下一篇: string 与char* char[]之间的转换(精)
起风啰
博客等级 码龄16年 30粉丝 4原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值