OPhone平台2D游戏引擎实现——场景、图层、元素(一)

上一篇我们对游戏引擎的框架进行了分析,同时也完成了一个基础的游戏引擎框架,那么这一篇我们将完成游戏引擎中的场景,图层,元素(节点)进行实现,但是在开始之前,我们可以对上一篇所介绍的框架进行完善,首先我们可以对引擎的渲染窗口(YFSGLSurfaceView)进行完善,使其具备事件处理能力和对渲染器(Renderer)进行控制。

首先在YFSGLSurfaceView中声明以下成员:
  1. //渲染器  
  2. private Director mDirector;  
  3. //事件调度器  
  4. private EventDispatcher mDispatcher;  
  5. 然后,需要在构造函数中实例化这些对象,代码如下:  
  6. //取得Director  
  7. this.mDirector = Director.getInstance();  
  8. //取得context  
  9. this.mDirector.context = context;  
  10. //取得EventDispatcher  
  11. this.mDispatcher = EventDispatcher.getInstance();  
  12. 同时在退出函数surfaceDestroyed中,我们将调用Director的onSurfaceDestroyed函数来处理退出操作,释放资源等,代码如下:  
  13. public void surfaceDestroyed(SurfaceHolder holder) {  
  14.        super.surfaceDestroyed(holder);  
  15.        this.mDirector.onSurfaceDestroyed();  
  16. }  
 
最后就是事件处理部分,由于我们对事件的处理都是在EventDispatcher中进行统一管理调度的,所以我们只需要按照不同的事件类型,将事件传递给EventDispatcher即可,EventDispatcher便会为我们进行事件处理,如代码清单2-1所示。
代码清单2-1:事件处理
  1. //触笔事件处理  
  2. public boolean onTouchEvent(MotionEvent event) {  
  3.     switch (event.getAction()) {  
  4.     case MotionEvent.ACTION_CANCEL:  
  5.     case MotionEvent.ACTION_OUTSIDE:  
  6.        this.mDispatcher.touchesCancelled(event);  
  7.        break;  
  8.     case MotionEvent.ACTION_DOWN:  
  9.        this.mDispatcher.touchesBegan(event);  
  10.        break;  
  11.     case MotionEvent.ACTION_MOVE:  
  12.        this.mDispatcher.touchesMoved(event);  
  13.        break;  
  14.     case MotionEvent.ACTION_UP:  
  15.        this.mDispatcher.touchesEnded(event);  
  16.     }  
  17.     return true;  
  18. }  
  19. //按键事件处理  
  20. public boolean onKeyDown(int keyCode, KeyEvent event) {  
  21.     if (this.mDispatcher.keyDown(event)) {  
  22.        return true;  
  23.     }  
  24.     return super.onKeyDown(keyCode, event);  
  25. }  
  26. public boolean onKeyUp(int keyCode, KeyEvent event) {  
  27.     if (this.mDispatcher.keyUp(event)) {  
  28.        return true;  
  29.     }  
  30.     return super.onKeyUp(keyCode, event);  
  31. }  
 
好了,上面则是我们对上一篇引擎的框架进行完善,当然还有Director需要完善,我们会在后面组件进行完善。这里我们先来分析最基础的场景元素,即前面说的节点。
 
场景元素
 
从名字大家可以看出来,所谓的场景元素,那么肯定就是能够放置在场景中的任何对象,比如一棵树、一个怪物、一个按钮,一个标签等都是场景中的元素,甚至场景、图层本身也是一个场景元素,即节点。我们将在后面给大家分析,大家也可以跳到后面去看一下,这样更有助于理解场景元素的定义。
 
节点基类实现
我们可以为这所有的元素抽象一个基类Node,任何可以在场景中放置的元素都将继承自该类。Node封装了各种元素的的通用逻辑和渲染过程,这里我们也把这些元素称之为“节点”,在后文中出现的节点就表示元素。首先我们要确定一个通用节点需要哪些属性?如代码清单2-2所示。
代码清单2-2:节点的部分属性
  1. public class Node {  
  2.     protected static final int INVALID_TAG = -1;  
  3.     //锚点比例  
  4.     protected PointF mAnchorPercent;  
  5.     //摄像头  
  6.     private Camera mCamera;  
  7.     //子节点  
  8.     protected ArrayList<Node> mChildren;  
  9.     //尺寸大小  
  10.     protected YFSSize mContentSize;  
  11.     private BaseGrid mGrid;  
  12.     //是否运行中...  
  13.     private boolean mRunning;  
  14.     //变换  
  15.     protected boolean mTransformDirty;  
  16.     protected boolean mInverseDirty;  
  17.     //父节点  
  18.     private Node mParent;  
  19.     //位置  
  20.     protected YFSPoint mPosition;  
  21.     //锚点是否发生作用  
  22.     private boolean mRelativeAnchorPoint;  
  23.     //是否显示  
  24.     private boolean mEnabled;  
  25.     //是否选中  
  26.     private boolean mSelected;  
  27.     //旋转,缩放  
  28.     private float mRotation;  
  29.     private float mScaleX;  
  30.     private float mScaleY;  
  31.     //tag标记  
  32.     protected int mTag;  
  33.     //放射变换  
  34.     private YFSAffineTransform mTransformMatrix;  
  35.     private YFSAffineTransform mInverseMatrix;  
  36.     //锚点位置  
  37.     protected YFSPoint mAnchorPosition;  
  38.     //其他附加数据  
  39.     private Object mUserData;  
  40.     //节点在OpenGL中的z order值  
  41.     private float mVertexZ;  
  42.     //是否可见  
  43.     protected boolean mVisible;  
  44.     //节点在父节点中的z order值  
  45.     private int mZOrder;  
  46.     //...  
  47. }  
 
代码清单2-2列出了一个通用节点所需要的常用属性,另外每个节点还可以设置其动画,我们将在引擎的动画部分完成之后在来完善节点的动画,大家可以根据注解来理解,这里我们主要说明一下“锚点”,我们对每一个节点都设置了一个锚点,比如一个节点的大小尺寸为480*320,锚点被设置为(0.5,0.5),那么锚点实际上所表示的就是(240,160)点的位置,那么我们在设置节点的位置坐标时,就会和这个锚点相关,如果我们将其中mRelativeAnchorPoint值设置为true,那么我们所设置的节点的位置就是这么锚点的相对位置,可能比较南里理解,我们在后面具体的使用过程中将进一步给大家分析。大家可以看都我们创建了一个子节点列表和一个父节点,因此说明我们的节点也是可以嵌套的。另一个需要分析的就是节点的摄像机Camera了,稍后将给大家详细分析,现在我们先来分析节点类的具体实现过程,由于篇幅关系对于该类的set和get函数就不进行解析了。
 
节点列表操作
首先我们来分析整个节点链,即子节点的一些常用操作,如代码清单2-3所示。
代码清单2-3:节点列表操作
  1. //添加节点  
  2. public Node addChild(Node child) {  
  3.     assert (child != null);  
  4.     return addChild(child, child.mZOrder, child.mTag);  
  5. }  
  6. //添加节点  
  7. public Node addChild(Node child, int z) {  
  8.     assert (child != null);  
  9.     return addChild(child, z, child.mTag);  
  10. }  
  11. //(节点,z值,tag标记)  
  12. public Node addChild(Node child, int z, int tag) {  
  13.     assert (child != null);  
  14.     assert (child.mParent == null);  
  15.     if (this.mChildren == null) {  
  16.        childrenAlloc();  
  17.     }  
  18.     insertChild(child, z);  
  19.     child.mTag = tag;  
  20.     child.setParent(this);  
  21.     if (this.mRunning) {  
  22.        child.onEnter();  
  23.     }  
  24.     return this;  
  25. }  
  26. //新建列表  
  27. private void childrenAlloc() {  
  28.     this.mChildren = new ArrayList(4);  
  29. }  
  30. //清理  
  31. public void cleanup() {  
  32.     if (this.mChildren != null)  
  33.        for (int i = 0; i < this.mChildren.size(); ++i)  
  34.            ((Node) this.mChildren.get(i)).cleanup();  
  35. }  
  36. //裁剪节点(移除节点)  
  37. private void detachChild(Node child, boolean doCleanup) {  
  38.     if (doCleanup) {//移除前判断是否需要清理  
  39.        child.cleanup();  
  40.     }  
  41.     child.setParent(null);  
  42.     this.mChildren.remove(child);  
  43. }  
  44. //得到节点  
  45. public Node getChild(int tag) {  
  46.     assert (tag != INVALID_TAG) : "Invalid tag";  
  47.     if (this.mChildren != null) {  
  48.        for (int i = 0; i < this.mChildren.size(); ++i) {  
  49.            Node child = (Node) this.mChildren.get(i);  
  50.            if (child.mTag == tag) {  
  51.               return child;  
  52.            }  
  53.        }  
  54.     }  
  55.     return null;  
  56. }  
  57. public ArrayList<Node> getChildren() {  
  58.     return this.mChildren;  
  59. }  
  60. //插入节点  
  61. private void insertChild(Node node, int z) {  
  62.     boolean added = false;  
  63.     for (int i = 0; i < this.mChildren.size(); ++i) {  
  64.        Node child = (Node) this.mChildren.get(i);  
  65.        if (child.getZOrder() > z) {  
  66.            added = true;  
  67.            this.mChildren.add(i, node);  
  68.            break;  
  69.        }  
  70.     }  
  71.     if (!added)  
  72.        this.mChildren.add(node);  
  73.     node.setZOrder(z);  
  74. }  
  75. //移除所有节点  
  76. public void removeAllChildren(boolean cleanup) {  
  77.     for (int i = 0; i < this.mChildren.size(); ++i) {  
  78.        Node child = (Node) this.mChildren.get(i);  
  79.        if (cleanup) {  
  80.            child.cleanup();  
  81.        }  
  82.        child.setParent(null);  
  83.     }  
  84.     this.mChildren.clear();  
  85. }  
  86. //移除节点  
  87. public void removeChild(Node child, boolean cleanup) {  
  88.     if (child == null) {  
  89.        return;  
  90.     }  
  91.     if (this.mChildren.contains(child))  
  92.        detachChild(child, cleanup);  
  93.     }  
  94. public void removeChild(int tag, boolean cleanup) {  
  95.     assert (tag != INVALID_TAG);  
  96.     Node child = getChild(tag);  
  97.     if (child == null)  
  98.        Log.w("Engine""removeChild: child not found");  
  99.     else  
  100.        removeChild(child, cleanup);  
  101. }  
  102. //重新排序节点  
  103. public void reorderChild(Node child, int zOrder) {  
  104.     assert (child != null) : "Child must be non-null";  
  105.     this.mChildren.remove(child);  
  106.     insertChild(child, zOrder);  
  107. }  
 
坐标转换系统
在上一篇文章我们说过,Android中坐标系和Opengl坐标系不一样,因此我们在使用Opengl ES做游戏时,时常需要对坐标系进行转换,说得更具体就是每一个元素在进行变换、事件处理时都需要将Android坐标系转换成Opengl坐标系,或者相反,因此我们在节点类中也对这些常用的处理进行了实现,对于坐标系的转换如代码清单2-4所示。
代码清单2-4:坐标系转换
  1. //把一个全局坐标转换成节点内坐标  
  2. public YFSPoint convertToNodeSpace(float x, float y) {  
  3.     YFSPoint worldPoint = YFSPoint.make(x, y);  
  4.     return worldPoint.applyTransform(worldToNodeTransform());  
  5. }  
  6. //把一个全局坐标转换成节点内坐标,以节点的锚点为原点  
  7. public YFSPoint convertToNodeSpaceAR(float x, float y) {  
  8.     YFSPoint nodePoint = convertToNodeSpace(x, y);  
  9.     return YFSPoint.sub(nodePoint, this.mAnchorPosition);  
  10. }  
  11. //把Android触摸事件中坐标转换成节点内坐标。  
  12. public YFSPoint convertTouchToNodeSpace(MotionEvent event) {  
  13.     YFSPoint point = Director.getInstance().convertToGL(event.getX(),  
  14.               event.getY());  
  15.     return convertToNodeSpace(point.x, point.y);  
  16. }  
  17. //把Android触摸事件中坐标转换成节点内坐标。  
  18. public YFSPoint convertTouchToNodeSpaceAR(MotionEvent event) {  
  19.     YFSPoint point = Director.getInstance().convertToGL(event.getX(),  
  20.               event.getY());  
  21.     return convertToNodeSpaceAR(point.x, point.y);  
  22. }  
  23. //把节点内坐标转换为OpenGL全局坐标  
  24. public YFSPoint convertToWorldSpace(float x, float y) {  
  25.     YFSPoint nodePoint = YFSPoint.make(x, y);  
  26.     return nodePoint.applyTransform(nodeToWorldTransform());  
  27. }  
  28. //把节点内坐标转换为OpenGL全局坐标。  
  29. public YFSPoint convertToWorldSpaceAR(float x, float y) {  
  30.     YFSPoint nodePoint = YFSPoint.make(x, y);  
  31.     nodePoint = YFSPoint.add(nodePoint, this.mAnchorPosition);  
  32.     return convertToWorldSpace(nodePoint.x, nodePoint.y);  
  33. }  
 
大家可以看到其实就是使用了我们上一篇文章所给大家介绍的YFSPoint的applyTransform操作,而最终则会通过YFSAffineTransform来进行转换操作,有了大家对坐标系的理解,相信转换这个坐标系就很简单了,我们不在重复叙述了,这里还需要介绍一个比较重要的操作,就是将矩阵转换成矩形,具体转换实现如代码清单2-5所示。
代码清单2-5:convertRectUsingMatrix实现
  1. private static YFSRect convertRectUsingMatrix(YFSRect aRect,  
  2.            YFSAffineTransform matrix) {  
  3.     YFSRect r = YFSRect.make(0.0F, 0.0F, 0.0F, 0.0F);  
  4.     YFSPoint[] p = new YFSPoint[4];  
  5.     for (int i = 0; i < 4; ++i) {  
  6.        p[i] = YFSPoint.make(aRect.origin.x, aRect.origin.y);  
  7.     }  
  8.     p[1].x += aRect.size.width;  
  9.     p[2].y += aRect.size.height;  
  10.     p[3].x += aRect.size.width;  
  11.     p[3].y += aRect.size.height;  
  12.     for (int i = 0; i < 4; ++i) {  
  13.        p[i] = p[i].applyTransform(matrix);  
  14.     }  
  15.     YFSPoint min = YFSPoint.make(p[0].x, p[0].y);  
  16.     YFSPoint max = YFSPoint.make(p[0].x, p[0].y);  
  17.     for (int i = 1; i < 4; ++i) {  
  18.        min.x = Math.min(min.x, p[i].x);  
  19.        min.y = Math.min(min.y, p[i].y);  
  20.        max.x = Math.max(max.x, p[i].x);  
  21.        max.y = Math.max(max.y, p[i].y);  
  22.     }  
  23.     r.origin.x = min.x;  
  24.     r.origin.y = min.y;  
  25.     r.size.width = (max.x - min.x);  
  26.     r.size.height = (max.y - min.y);  
  27.     return r;  
  28. }  
 
由于我们的节点可以嵌套,所以节点的位置也就会受到其父节点的影响,因此我们提供了得到相对位置的函数,如代码清单2-6所示。
代码清单2-6:getAbsolutePosition实现
  1. public YFSPoint getAbsolutePosition() {  
  2.     YFSPoint ret = YFSPoint.make(this.mPosition.x, this.mPosition.y);  
  3.     Node cn = this;  
  4.     while (cn.mParent != null) {  
  5.        cn = cn.mParent;  
  6.        ret.x += cn.mPosition.x;  
  7.        ret.y += cn.mPosition.y;  
  8.     }  
  9.     return ret;  
  10. }  
 
其实现原理非常简单,我们判断其父节点是否为NULL,如果不围NULL,则在当前节点位置的基础上加上父节点的位置,就可以得到相对于父节点的位置。有了这些方法,我们在使用节点时就可以很方便了,这些操作需要一些数学方面的知识,大家在看的同时,可以参考一些数学资料,这样将有助于理解。
 
碰撞包围盒
当场景元素作为游戏开发的sprite时,我们很可能会对其进行碰撞检测,因此我们需要对节点设置其包围盒,以方便我们进行碰撞检测,如代码清单2-7所示,计算我们为每个节点设置的包围盒。
代码清单2-7:节点包围盒
//得到包围盒,相对于节点自身坐标系而言
  1. public YFSRect getBoundingBox() {  
  2.     return YFSRect.make(0.0F, 0.0F, this.mContentSize.width,  
  3.               this.mContentSize.height);  
  4. }  
  5. // 得到节点的边框矩形,相对于父节点坐标系而言  
  6. public YFSRect getBoundingBoxRelativeToParent() {  
  7.     YFSRect rect = YFSRect.make(0.0F, 0.0F, this.mContentSize.width,  
  8.               this.mContentSize.height);  
  9.     return convertRectUsingMatrix(rect, nodeToParentTransform());  
  10. }  
  11. //得到节点的边框矩形,相对于全局坐标系而言,得到的矩形对象中,origin是左下角坐标  
  12. public YFSRect getBoundingBoxReletiveToWorld() {  
  13.     YFSRect rect = YFSRect.make(0.0F, 0.0F, this.mContentSize.width,  
  14.               this.mContentSize.height);  
  15.     return convertRectUsingMatrix(rect, nodeToWorldTransform());  
  16. }  
这里我们主要是取得节点的包围盒,具体的碰撞检测实现另作讨论,当然最简单的就是直接使用一些2D的物理引擎,比如:box2d和chipmunk,同样你也可以自己实现碰撞算法,这里需要说明的是我们提供了3个不同环境的包围盒,他们包括自身包围盒、相对父节点的包围盒、相对于全局坐标系的包围盒。
 
渲染系统
对一些常用的操作进行了实现之后,我们就开始渲染这些节点对象了,渲染时我们同样需要对该节点及其所有子节点都进行渲染,由于该类是任何节点的基类,所以我们并不需要真正去实现渲染的(draw函数),这些渲染我们将在节点的具体子类中去实现,这部分内容我们会在后面的组件一节中进行介绍,这里我们主要是实现一个渲染的流程,我们先来看看具体代码实现,在进行分析,如代码清单2-8所示。
代码清单2-8:渲染流程
  1. //强制渲染当前节点,包括子节点  
  2. public void visit(GL10 gl) {  
  3.     //是否可见  
  4.     if (!this.mVisible) {  
  5.        return;  
  6.     }  
  7.     //把当前的矩阵拷贝到栈中.  
  8.     gl.glPushMatrix();  
  9.     //变换操作  
  10.     transform(gl);  
  11.     //渲染子节点  
  12.     if (this.mChildren != null) {  
  13.        for (int i = 0; i < this.mChildren.size(); ++i) {  
  14.            Node child = (Node) this.mChildren.get(i);  
  15.            if (child.mZOrder >= 0)  
  16.               break;  
  17.            child.visit(gl);  
  18.        }  
  19.     }  
  20.     draw(gl);  
  21.     if (this.mChildren != null) {  
  22.        for (int i = 0; i < this.mChildren.size(); ++i) {  
  23.            Node child = (Node) this.mChildren.get(i);  
  24.            if (child.mZOrder >= 0) {  
  25.               child.visit(gl);  
  26.            }  
  27.        }  
  28.     }  
  29.     //回复渲染之前的矩阵,用最后压入栈的矩阵恢复为当前矩阵  
  30.     gl.glPopMatrix();  
  31. }  
 
从代码清单2-8中可以看出,节点的渲染流程为,首先判断节点时候可见,然后通过glPushMatrix将当前矩阵压入到栈中,即保存当然矩阵,以便恢复,然后进行节点的变换操作,最后才渲染节点及其子节点,完成之后需要通过glPopMatrix来恢复之前压入栈顶的矩阵。
 
知道了渲染的流程,下面我们来分析如何处理节点的变换操作,其实节点的变换操作在Opengl中就主要指旋转、缩放、平移等,其具体实现入代码清单2-9所示。
代码清单2-9:变换操作
  1. //变换操作  
  2. protected void transform(GL10 gl) {  
  3.     //如果与锚点有关,则需要处理  
  4.     if (this.mRelativeAnchorPoint) {  
  5.        gl.glTranslatef(-this.mAnchorPosition.x, -this.mAnchorPosition.y,  
  6.                   this.mVertexZ);  
  7.     }  
  8.     //平移  
  9.     gl.glTranslatef(this.mPosition.x + this.mAnchorPosition.x,  
  10.            this.mPosition.y + this.mAnchorPosition.y, this.mVertexZ);  
  11.     //旋转  
  12.     if (this.mRotation != 0.0F) {  
  13.        gl.glRotatef(-this.mRotation, 0.0F, 0.0F, 1.0F);  
  14.     }  
  15.     //缩放  
  16.     if ((this.mScaleX != 1.0F) || (this.mScaleY != 1.0F)) {  
  17.        gl.glScalef(this.mScaleX, this.mScaleY, 1.0F);  
  18.     }  
  19.     //如果与锚点有关...  
  20.     gl.glTranslatef(-this.mAnchorPosition.x, -this.mAnchorPosition.y,  
  21.               this.mVertexZ);  
  22. }  
 
在Opengl中旋转操作使用glRotatef、平移操作使用glTranslatef、缩放操作则使用glRotatef,这都很简单,唯一需要说明一下的是,如果我们的节点的mRelativeAnchorPoint为true则表示与锚点有关,那么我们在变换钱就需要先按照描点的相对位置来变换,然后进行节点自身的变换,最后再次按照描点的相对位置来进行平移变换。这样才能对节点渲染的位置进行准确的控制。
为了方便我们在对节点进行激活时进行某些操作,我们实现了onEnter和onExit接口,他们分别是当节点要变成活动态时,该方法被调用和当节点所属场景退出时该方法被调用,或者当该节点被删除时被调用,具体实现入代码清单2-10所示。
代码清单2-10:onEnter和onExit实现
  1. public void onEnter() {  
  2.     if (this.mChildren != null) {  
  3.        for (int i = 0; i < this.mChildren.size(); ++i) {  
  4.            Node child = (Node) this.mChildren.get(i);  
  5.            child.onEnter();  
  6.        }  
  7.     }  
  8.     this.mRunning = true;  
  9. }  
  10. public void onExit() {  
  11.     this.mRunning = false;  
  12.     if (this.mChildren != null)  
  13.        for (int i = 0; i < this.mChildren.size(); ++i) {  
  14.            Node child = (Node) this.mChildren.get(i);  
  15.            child.onExit();  
  16.        }  
  17. }  
 
实现了这样的接口之后,我们就可以在节点被添加(addnode)时调用onEnter函数,在将节点被移除时调用onExit函数,因此前面所实现的节点操作函数中就可以加上这样两个函数,使其更加完善,添加方法很简单,就是判断当前节点是否运行(mRunning),如果运行则调用该节点的上面这两个函数,所以大家可以自己下去实现即可。
 
其他接口
为了节点的属性更加完善,我们还提供了一些附加的接口,用来设置节点的特殊属性,比如:动画、颜色、透明度、贴图等。当前我们并不需要实现这些接口,但是我们这里先给定义出来,在具体的组件,节点子类中可能就会需要,其接口定义如代码清单2-11所示。
代码清单2-11:其他接口实现
  1. //动画帧的基类  
  2. public static class Frame {  
  3.     public float duration;  
  4.     public Frame(float duration) {  
  5.        this.duration = duration;  
  6.     }  
  7. }  
  8. //动画接口  
  9. public static abstract interface IAnimation {  
  10.     //时间  
  11.     public abstract float getDuration();  
  12.     //德奥帧  
  13.     public abstract List<? extends Node.Frame> getFrames();  
  14.     //得到名称  
  15.     public abstract String getName();  
  16. }  
  17. //实现此接口表示节点可以设置渲染模式  
  18. public static abstract interface IBlendable {  
  19.     //设置混合  
  20.     public abstract void setBlendFunc(YFSBlendFunc paramYFSBlendFunc);  
  21.     //得到混合  
  22.     public abstract YFSBlendFunc getBlendFunc();  
  23. }  
  24. //实现该接口表示节点可以支持设置贴图且设置渲染方式  
  25. public static abstract interface IBlendableTextureOwner extends  
  26.            Node.IBlendable, Node.ITextureOwner {  
  27. }  
  28. //一个组合接口,表示支持设置颜色和透明度  
  29. public static abstract interface IColorable extends Node.ITransparent,  
  30.            Node.IRGB {  
  31. }  
  32. //一个组合接口,表示支持设置颜色和文字  
  33. public static abstract interface IColorableLabel extends Node.IColorable, Node.ILabel {  
  34. }  
  35. //实现此接口表示支持对单帧进行操作, 并且支持添加动画  
  36. public static abstract interface IFrames {  
  37. //添加动画  
  38.     public abstract void addAnimation(Node.IAnimation paramIAnimation);  
  39.     //得到动画名称  
  40.     public abstract Node.IAnimation getAnimationByName(String paramString);  
  41.     public abstract Node.Frame getDisplayFrame();  
  42.     //是否显示  
  43.     public abstract boolean isFrameDisplayed(Node.Frame paramFrame);  
  44.     //设置显示帧  
  45.     public abstract void setDisplayFrame(Node.Frame paramFrame);  
  46.     public abstract void setDisplayFrame(String paramString, int paramInt);  
  47. }  
  48. //实现此接口表示节点支持设置文本  
  49. public static abstract interface ILabel {  
  50.     public abstract void setText(String paramString);  
  51. }  
  52. //实现此接口表示节点支持设置颜色  
  53. public static abstract interface IRGB {  
  54.     public abstract YFSColor3B getColor();  
  55.     public abstract void setColor(YFSColor3B paramYFSColor3B);  
  56. }  
  57. //实现此接口表示可以得到大小信息  
  58. public static abstract interface ISizable {  
  59.     public abstract float getHeight();  
  60.     public abstract float getWidth();  
  61. }  
  62. //实现此接口表示节点可以包含一个贴图  
  63. public static abstract interface ITextureOwner {  
  64.     public abstract Texture2D getTexture();  
  65.     public abstract void setTexture(Texture2D paramTexture2D);  
  66. }  
  67. //实现此接口表示节点支持设置透明度  
  68. public static abstract interface ITransparent {  
  69.     public abstract int getAlpha();  
  70.     public abstract void setAlpha(int paramInt);  
  71. }  
 
大家可以先对这些接口有一定的了解,后面具体实现时将对其进行详细介绍,另外该类还需要包含一些set和get属性的函数,这就非常简单了,大家可以查看我们提供的源码即可。最后该类还需要包含一个摄像头和一个计时器,这两部分内容比较重要我们将在本文末单独来进行介绍,如果你需要了解,也可以跳到后面去查看。
 
游戏场景
上面我们介绍了场景的节点,现在我们来学习场景,我们只所以把场景放在后面来介绍,就是因为该引擎中场景就是一个继承自节点(Node)类的一个子类,为什么会这样呢?这将和我们所定义的场景的定义相关。那么场景究竟是什么呢?
当我们在做一个游戏时,一般都有很多个界面,其实我们这里是把这每一个界面都看成是一个场景,界面中的任何元素也就是我们的场景元素,也就是节点(Node),由于我们前面知道了,节点事可以嵌套的,可以包含很多个子节点,所有这正好符合我们场景原则,场景中可以包含很多和节点元素。这样来看,大家应该能够理解了吧。因此在有了节点的基础上实现场景就很简单了,首先我们来看具体的代码,如代码清单2-12所示。
代码清单2-12:场景scene实现
  1. public class Scene extends Node {  
  2.     //取得scene实例  
  3.     public static Scene make() {  
  4.        return new Scene();  
  5.     }  
  6.     protected Scene() {  
  7.        //得到窗口尺寸  
  8.        YFSSize s = Director.getInstance().getWindowSize();  
  9.        //设置不受锚点影响  
  10.        setRelativeAnchorPoint(false);  
  11.        //设置锚点  
  12.        setAnchorPercent(0.5F, 0.5F);  
  13.        //设置尺寸  
  14.        setContentSize(s.width, s.height);  
  15.     }  
  16. }  
 
整个引擎的场景scene类就这么简单,他继承自Node类,因此具有Node类的通用方法,场景与一般节点位移不同的就是,我们在构造函数中设置锚点时,将锚点设置为(0.5,0.5)了,一般节点默认的锚点为(0,0),所以这也正是场景和一般节点不同,并且需要注意的地方,最后将场景的尺寸设置为窗口的尺寸即可。这样我们可以在场景中添加其他任何节点,作为该场景节点的子节点。

待续(二)

(声明:本网的新闻及文章版权均属OPhone SDN网站所有,如需转载请与我们编辑团队联系。任何媒体、网站或个人未经本网书面协议授权,不得进行任何形式的转载。已经取得本网协议授权的媒体、网站,在转载使用时请注明稿件来源。)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值