当前位置:   article > 正文

OPhone平台2D游戏引擎实现——概述及框架(一)_2d图元引擎架构

2d图元引擎架构

    http://www.ophonesdn.com/article/show/283

      很高兴在这里又和大家见面了,为大家带来的是Ophone平台2D游戏引擎实现,虽然现在3D的游戏越来越多,但是2D游戏依然倍受欢迎,为了方便我们开发更多精彩的2D游戏,这里我们将讨论如何来实现一个OPhone平台的2D游戏引擎,对于3D游戏及其引擎实现,我们会再抽出时间来为大家介绍。相信大家经过这么长时间的学习,对于基础知识也有一定的把握,所以我们这一系列文章将介绍更高级的内容,大家也可以参考Opengl官方网站进行更多的学习。祝大家学习愉快!

先回顾一下09年度OphoneSDN征文大赛,我们所带来的是“深入浅出Ophone平台游戏开发”,在这两篇文章中,我们完成了一个风魔全球的最经典的游戏俄罗斯方块,但是我们所使用的技术主要包括Ophone平台的Skia图形库和一些常用的组件,大家也看到了在Ophone平台中运行这类小游戏并不需要担心效率问题,但是大家同时也注意到了,我们的游戏并没有太多的动画和特效,没有动画和特效的游戏就犹如在记事本中绘制图形一样,让人很郁闷,不但效果不好,而且需要花费大量时间、精力。这肯定不是我们所想要的,同时在我们加入了动画和特效之后,游戏能否流畅运行?经过测试效果很不满意,所以我们需要一种更先进的技术来处理动画和特效,在保证游戏能顺利运行的前提下,使游戏更加生动,那便是本次我们所带来的“利用3D技术来开发2D游戏”。Ophone平台所提供的3D渲染图形库便是我们熟悉的Opengl ES,下面我们就将使用Opengl ES来构架一个游戏引擎。
在开始之前,我们给大家演示一下,利用我们实现的引擎来完成的一个游戏半成品。如图1所示为游戏的启动画面,图2为主菜单界面,图3为游戏中的界面,每个界面之间的切换都会有相应的动画效果,游戏中也有各种“精灵”的实现,更多游戏相关截图请参见文末的附图。
图1 启动画面
 
 
图2 游戏主界面
 
图3 游戏中界面
 
大家已经看到了我们的半成品游戏,请暂且相信我们的实现的引擎能够很轻松的完成Ophone平台2D游戏的开发(当然稍加调整同样可以开发3D游戏),同时当你看完这一系列文章之后,你同样可以实现自己的游戏引擎并且能开发出绚丽多彩的游戏。因为笔者发现有很多人在问Opengl是什么?能做什么的问题?所以在开始引擎实现的学习之前,我们首先对Opengl ES进行一个简单的介绍。
 
Opengl ES概述
OpenGL ES (OpenGL for Embedded Systems) 是 OpenGL 三维图形 API 的子集,针对手机、PDA和游戏主机等嵌入式设备而设计。该API由Khronos集团定义推广,Khronos是一个图形软硬件行业协会,该协会主要关注图形和多媒体方面的开放标准。
OpenGL ES 是从 OpenGL 裁剪定制而来的,去除了 glBegin/glEnd,四边形(GL_QUADS)、多边形(GL_POLYGONS)等复杂图元等许多非绝对必要的特性。经过多年发展,现在主要有两个版本,OpenGL ES 1.x 针对固定管线硬件的,OpenGL ES 2.x 针对可编程管线硬件。OpenGL ES 1.0 是以 OpenGL 1.3 规范为基础的,OpenGL ES 1.1 是以 OpenGL 1.5 规范为基础的,它们分别又支持 common 和 common lite 两种profile。lite profile只支持定点定点实数,而common profile既支持定点数又支持浮点数。 OpenGL ES 2.0 则是参照 OpenGL 2.0 规范定义的,common profile发布于2005-8,引入了对可编程管线的支持。
下面是我们列举的一些被Opengl ES裁剪掉的Opengl功能,因此大家在使用api时需要注意,不能使用以下这些api:
 
1.      glBegin/glEnd

2.       glArrayElement

3.       显示列表

4.       求值器

5.       索引色模式

6.       自定义裁剪平面

7.       glRect

8.       图像处理(这个一般显卡也没有,FireGL/Quadro显卡有)

9.       反馈缓冲

10.   选择缓冲

11.   累积缓冲

12.   边界标志

13.   glPolygonMode

14.   GL_QUADS、GL_QUAD_STRIP、GL_POLYGON
15.   glPushAttrib、PopAttrib、glPushClientAttrib、glPopClientAttrib

16.   TEXTURE_1D、TEXTURE_3D、TEXTURE_RECT、TEXTURE_CUBE_MAP

17.   GL_COMBINE

18.   自动纹理坐标生成

19.   纹理边界

20.   GL_CLAMP、GL_CLAMP_TO_BORDER

21.   消失纹理代表

22.   纹理LOD限定

23.   纹理偏好限定

24.   纹理自动压缩、解压缩

25.   glDrawPixels、glPixelTransfer、glPixelZoom

26.   glReadBuffer、glDrawBuffer、glCopyPixels
由于篇幅关系,这里我们列举的可能不是全部,更多详细内容大家可以参考 www.khronos.org/opengles/
 
引擎结构划分
 
下面我们开始进入游戏引擎的实现学习,在构架一个游戏引擎之前,我们需要对引擎的功能进行确定和划分,这里我们讲引擎划分为以下几个模块:
  •  核心框架(包括数据类型、异常处理、事件处理)
  •  场景、图层、场景元素(节点)
  •  贴图/理、精灵
  •  文字渲染
  •  常用组件
  •  动画系统
  •  地图系统
  •  粒子系统
  •  游戏实例
 
我们也会按照这里过程来进行学习,当然要使引擎更加完善,则还需要音乐,音效、数据存储等子系统,这里我们主要讨论游戏引擎的图形渲染部分,最后会有一个前面我们演示的游戏实例!其他的我们有机会在讨论。
 
坐标系
坐标系是我们所讨论的第一个问题,因为在Ophone平台中的缺省坐标系是原点在左上角,x 和 y 轴的正向是往右和往下。而 OpenGL 的坐标系是原点在左下角x 和 y 轴的正向是往右和往上。所以我们首先需要明白该我们这里所使用的坐标系,明确作原远点的所在地,然后更多坐标系相关问题,我们后面会陆续给大家介绍。
 
游戏引擎框架
一个好的系统或者应用程序都离不开一个优秀的构架,下面我们将分析如何合理的构架一个Ophone平台的游戏引擎框架.这里需要说明一下,笔者在构架时参考了Iphone平台著名的2D游戏引擎cocos2D的构架,在这里也对该引擎的作者表示感谢.因此如果你熟悉Iphone平台并且熟悉cocos2D,将会很容易的理解本课引擎的框架,如果不熟悉,也不必担心,我们会详细的分析每一个模块。用Opengl来渲染图形,首先就需要创建一个于底层硬件相关的用于显示图形的窗口和渲染器,该过程本身比较复杂,但是Ophone平台为我们系统了GLSurfaceView类用来创建一个窗口GLSurfaceView.Renderer用来构建一个渲染器,这使得我们可以很轻松的完成这一工作。下面我们首先看一下窗口的创建,这里我们为了让大家对GLSurfaceView的实现理解得更深刻,我们创建一个GLSurfaceView的子类YFSGLSurfaceView来作为我们的渲染窗口,整个类的实现非常简单,如代码清单1-1所示。
  1. 代码清单1-1:YFSGLSurfaceView类实现  
  2. public class YFSGLSurfaceView extends GLSurfaceView {  
  3.     public YFSGLSurfaceView(Context context) {  
  4.        this(context, null);  
  5.     }  
  6.     public YFSGLSurfaceView(Context context, AttributeSet attrs) {  
  7.        super(context, attrs);  
  8.        // 设置Surface格式  
  9.        setEGLConfigChooser(8888160);  
  10.        // 设置Renderer  
  11.        setRenderer(this.mDirector);  
  12.        getHolder().setFormat(PixelFormat.TRANSLUCENT);  
  13.        //允许触发事件  
  14.        setFocusable(true);  
  15.        setFocusableInTouchMode(true);  
  16.     }  
  17.     //窗口销毁时触发  
  18.     public void surfaceDestroyed(SurfaceHolder holder) {  
  19.        super.surfaceDestroyed(holder);  
  20.     }  
  21.     public boolean onTouchEvent(MotionEvent event) {  
  22.        //触笔事件处理  
  23.        return true;  
  24.     }  
  25.     //按键事件处理  
  26.     public boolean onKeyDown(int keyCode, KeyEvent event) {  
  27.        return super.onKeyDown(keyCode, event);  
  28.     }  
  29.     public boolean onKeyUp(int keyCode, KeyEvent event) {  
  30.        return super.onKeyUp(keyCode, event);  
  31.     }  
  32. }  
 
 
代码清单1-1,我们便在Ophone平台上实现了一个Opengl渲染窗口,大部分地方我们都加入了注解,相信大家都能看明白,这里我们重点说明一下setEGLConfigChooser函数,其参数包括了rgba的颜色尺寸和深度缓冲,模板缓冲的尺寸,最主要应该放在setRenderer函数中,它用于为该渲染窗口设置一个渲染器来渲染我们绘制的图形。该图形渲染器我们同样需要自己来实现,前面说过可以采用GLSurfaceView.Renderer来实现渲染器,下面我们将创建一个Renderer的子类来作为该环境的渲染器。根据cocos2D的经验,我们将渲染器命名为Director,大家可以从名称看出来,Director扮演着一个非常重要的角色,如代码清单1-2所示。
 
代码清单1-2:Director构架实现
  1. public class Director implements GLSurfaceView.Renderer {  
  2.     private static Director sInstance;  
  3.     //投影的类型,分为2D,3D等  
  4.     public static final int PROJECTION_2D = 1;  
  5.     public static final int PROJECTION_3D = 2;  
  6.     public static final int PROJECTION_CUSTOM = 3;  
  7.     //默认为2D  
  8.     public static final int PROJECTION_DEFAULT = PROJECTION_2D;  
  9.     public Context context = null;  
  10.     public GL10 gl = null;  
  11.     public GL11 gl11 = null;  
  12.     public GL11ExtensionPack gl11ext = null;  
  13.     private GLSurfaceView mGLView;  
  14.     private long mLastUpdateTime;  
  15.     private boolean mPaused;  
  16.     private int mProjection;  
  17.     private int mWidth;  
  18.     private int mHeight;  
  19.     private DisplayMetrics mDM;  
  20.     private List<IDestroyable> mDestroyables;  
  21.     //生命周期  
  22.     private List<IDirectorLifecycleObserver> mLifecycleObservers;  
  23.     //取得本类对象  
  24.     public static Director getInstance() {  
  25.        synchronized (Director.class) {  
  26.            if (sInstance == null) {  
  27.               sInstance = new Director();  
  28.            }  
  29.            return sInstance;  
  30.        }  
  31.     }  
  32.     protected Director() {  
  33.        synchronized (Director.class) {  
  34.            this.mPaused = false;  
  35.            this.mDestroyables = new ArrayList();  
  36.            this.mLifecycleObservers = new ArrayList();  
  37.        }  
  38.     }  
  39.     //生命周期相关  
  40.     public void addLifecycleObserver(IDirectorLifecycleObserver observer) {  
  41.        this.mLifecycleObservers.add(observer);  
  42.     }  
  43.     public void removeLifecycleObserver(IDirectorLifecycleObserver observer) {  
  44.        this.mLifecycleObservers.remove(observer);  
  45.     }  
  46.     public void registerDestroyable(IDestroyable destroyable) {  
  47.        this.mDestroyables.add(destroyable);  
  48.     }  
  49.     public void unregisterDestroyable(IDestroyable destroyable) {  
  50.        this.mDestroyables.remove(destroyable);  
  51.     }  
  52.     //激活GL视图  
  53.     public boolean attachInView(GLSurfaceView view) {  
  54.        return initOpenGLViewWithView(view);  
  55.     }  
  56.     //获取屏幕的相关属性  
  57.     public DisplayMetrics getDisplayMetrics() {  
  58.        if (this.mDM == null) {  
  59.            this.mDM = this.context.getResources().getDisplayMetrics();  
  60.        }  
  61.        return this.mDM;  
  62.     }  
  63.     //计算fps  
  64.     private void calculateDeltaTime() {  
  65.        long now = SystemClock.uptimeMillis();  
  66.        if (this.mLastUpdateTime == 0L) {  
  67.            this.mDelta = 0.0F;  
  68.        } else {  
  69.            this.mDelta = ((float) (now - this.mLastUpdateTime) / 1000.0F);  
  70.            this.mDelta = Math.max(0.0F, this.mDelta);  
  71.        }  
  72.        this.mLastUpdateTime = now;  
  73.     }  
  74.     //退出处理  
  75.     private boolean detach() {  
  76.        try {  
  77.            if ((this.context instanceof Activity)  
  78.                   && (!((Activity) this.context).isFinishing()))  
  79.               ((Activity) this.context).finish();  
  80.            return true;  
  81.        } catch (Exception e) {  
  82.        }  
  83.        return false;  
  84.     }  
  85.     //结束时释放空间  
  86.     public void end() {  
  87.        if ((this.mGLView != null) && (!this.mPaused)) {  
  88.            this.mGLView.onPause();  
  89.        }  
  90.        for (IDirectorLifecycleObserver ob : this.mLifecycleObservers)  
  91.            ob.onDirectorEnd();  
  92.        this.mLifecycleObservers.clear();  
  93.        this.mDM = null;  
  94.        this.gl = null;  
  95.        this.gl11 = null;  
  96.        this.gl11ext = null;  
  97.        for (IDestroyable destroyable : this.mDestroyables)  
  98.            destroyable.destroy();  
  99.        this.mDestroyables.clear();  
  100.        detach();  
  101.        this.context = null;  
  102.        this.mGLView = null;  
  103.        sInstance = null;  
  104.        //释放所有资源,内存空间,,包括后面将介绍的贴图,scene,layer等  
  105.     }  
  106.     public Context getContext() {  
  107.        return this.context;  
  108.     }  
  109.     //取得屏幕方向  
  110.     public boolean isPortrait() {  
  111.        Display display = ((WindowManager) this.context  
  112.               .getSystemService("window")).getDefaultDisplay();  
  113.        return display.getOrientation() == 0;  
  114.     }  
  115.     //得到GL视图  
  116.     public GLSurfaceView getOpenGLView() {  
  117.        return this.mGLView;  
  118.     }  
  119.     //得到投影类型  
  120.     public int getProjection() {  
  121.        return this.mProjection;  
  122.     }  
  123.     //初始化GL的默认属性  
  124.     private void initGLDefaultValues(GL10 gl) {  
  125.        this.gl = gl;  
  126.        if (gl instanceof GL11)  
  127.            this.gl11 = ((GL11) gl);  
  128.        if (gl instanceof GL11ExtensionPack) {  
  129.            this.gl11ext = ((GL11ExtensionPack) gl);  
  130.        }  
  131.        //开启抖动  
  132.        gl.glDisable(GL10.GL_DITHER);  
  133.        //设置插值算法及其渲染模式  
  134.        gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);  
  135.        //设置是否开启混合(是)  
  136.        setAlphaBlending(true);  
  137.        //设置混色的方式  
  138.        gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);  
  139.        //开启光效  
  140.        gl.glDisable(GL10.GL_LIGHTING);  
  141.        //设置渲染模式  
  142.        gl.glShadeModel(GL10.GL_FLAT);  
  143.        //设置清理屏幕颜色rgba  
  144.        gl.glClearColor(0.0F, 0.0F, 0.6F, 0.0F);  
  145.     }  
  146.     //根据GLSurfaceView来初始化GL视图  
  147.     private boolean initOpenGLViewWithView(GLSurfaceView view) {  
  148.        this.mGLView = view;  
  149.        DisplayMetrics dm = getDisplayMetrics();  
  150.        this.mWidth = dm.widthPixels;  
  151.        this.mHeight = dm.heightPixels;  
  152.        EventDispatcher.getInstance().setDispatchEvents(true);  
  153.        return true;  
  154.     }  
  155.     //判断是否被激活  
  156.     public boolean isOpenGLAttached() {  
  157.        return this.mGLView != null;  
  158.     }  
  159.     //渲染  
  160.     public void onDrawFrame(GL10 gl) {  
  161.        if (this.mGLView == null) {  
  162.            return;  
  163.        }  
  164.        //允许设置顶点数组,颜色数组,纹理坐标数组  
  165.        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);  
  166.        gl.glDisableClientState(GL10.GL_COLOR_ARRAY);  
  167.        gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);  
  168.        //允许2D贴图  
  169.        gl.glDisable(GL10.GL_TEXTURE_2D);  
  170.        //清理屏幕(颜色缓存,深度缓存)  
  171.        gl.glClear(GL10.GL_COLOR_BUFFER_BIT|GL10.GL_DEPTH_BUFFER_BIT);  
  172.        //计算fps  
  173.        calculateDeltaTime();  
  174.        if (this.mPaused)  
  175.            return;  
  176.     }  
  177.     //屏幕窗口大小改变  
  178.     public void onSurfaceChanged(GL10 gl, int width, int height) {  
  179.        this.mWidth = width;  
  180.        this.mHeight = height;  
  181.        gl.glViewport(00, width, height);  
  182.        setDefaultProjection();  
  183.     }  
  184.     //窗口创建时  
  185.     public void onSurfaceCreated(GL10 gl, EGLConfig config) {  
  186.        initGLDefaultValues(gl);  
  187.     }  
  188.     //销毁时  
  189.     public void onSurfaceDestroyed() {  
  190.     }  
  191.     //暂停处理  
  192.     public void pause() {  
  193.        if (this.mPaused) {  
  194.            return;  
  195.        }  
  196.        if (this.mGLView != null)  
  197.            this.mGLView.onPause();  
  198.        this.mPaused = true;  
  199.        for (IDirectorLifecycleObserver ob : this.mLifecycleObservers)  
  200.            ob.onDirectorPause();  
  201.     }  
  202.     //中断之后重新激活  
  203.     public void resume() {  
  204.        if (!this.mPaused) {  
  205.            return;  
  206.        }  
  207.        this.mLastUpdateTime = SystemClock.uptimeMillis();  
  208.        if (this.mGLView != null) {  
  209.            this.mGLView.onResume();  
  210.        }  
  211.        this.mPaused = false;  
  212.        this.mDelta = 0.0F;  
  213.        for (IDirectorLifecycleObserver ob : this.mLifecycleObservers)  
  214.            ob.onDirectorResume();  
  215.     }  
  216.     //设置投影类型为2D  
  217.     public void set2DProjection() {  
  218.        setProjection(PROJECTION_2D);  
  219.     }  
  220.     //设置投影类型为3D  
  221.     public void set3DProjection() {  
  222.        setProjection(PROJECTION_3D);  
  223.     }  
  224.     //设置是否开启混合  
  225.     public void setAlphaBlending(boolean on) {  
  226.        if (on)  
  227.            this.gl.glEnable(GL10.GL_BLEND);  
  228.        else  
  229.            this.gl.glDisable(GL10.GL_BLEND);  
  230.     }  
  231.     //设置默认投影方式  
  232.     private void setDefaultProjection() {  
  233.        setProjection(PROJECTION_DEFAULT);  
  234.     }  
  235.     //设置深度测试  
  236.     public void setDepthTest(boolean on) {  
  237.        if (on) {  
  238.            this.gl.glEnable(GL10.GL_DEPTH_TEST);  
  239.            this.gl.glDepthFunc(GL10.GL_LEQUAL);  
  240.            this.gl.glDepthRangef(0.0F, 1.0F);  
  241.            this.gl.glClearDepthf(1.0F);  
  242.        } else {  
  243.            this.gl.glDisable(GL10.GL_DEPTH_TEST);  
  244.        }  
  245.     }  
  246.     //设置双面显示,,,背面裁切  
  247.     public void setCullFace(boolean on) {  
  248.        this.gl.glFrontFace(GL10.GL_CCW);  
  249.        this.gl.glEnable(GL10.GL_CULL_FACE);  
  250.        this.gl.glCullFace(GL10.GL_BACK);  
  251.     }  
  252.     //设置投影类型  
  253.     public void setProjection(int projection) {  
  254.        switch (projection) {  
  255.        case PROJECTION_2D:  
  256.            this.gl.glMatrixMode(GL10.GL_PROJECTION);  
  257.            this.gl.glLoadIdentity();  
  258.            //正交  
  259.            this.gl.glOrthof(0.0F, this.mWidth, 0.0F, this.mHeight, -1.0F, 1.0F);  
  260.            this.gl.glMatrixMode(GL10.GL_MODELVIEW);  
  261.            this.gl.glLoadIdentity();  
  262.            setDepthTest(false);  
  263.            break;  
  264.        case PROJECTION_3D:  
  265.            this.gl.glViewport(00this.mWidth, this.mHeight);  
  266.            this.gl.glMatrixMode(GL10.GL_MODELVIEW);  
  267.            this.gl.glLoadIdentity();  
  268.            //透视  
  269.            GLU.gluPerspective(this.gl, 60.0F, this.mWidth / this.mHeight,  
  270.                   0.5F, 1500.0F);  
  271.            this.gl.glMatrixMode(GL10.GL_MODELVIEW);  
  272.            this.gl.glLoadIdentity();  
  273.            //lookat  
  274.            GLU.gluLookAt(this.gl, this.mWidth / 2this.mHeight / 2,  
  275.                   Camera.getZEye(), this.mWidth / 2this.mHeight / 20.0F,  
  276.                   0.0F, 1.0F, 0.0F);  
  277.            setDepthTest(true);  
  278.            break;  
  279.        case PROJECTION_CUSTOM:  
  280.            break;  
  281.        }  
  282.        this.mProjection = projection;  
  283.     }  
  284.     //销毁接口  
  285.     public static abstract interface IDestroyable {  
  286.        public abstract void destroy();  
  287.     }  
  288.     //生命周期接口  
  289.     public static abstract interface IDirectorLifecycleObserver {  
  290.        public abstract void onDirectorPause();  
  291.        public abstract void onDirectorResume();  
  292.        public abstract void onDirectorEnd();  
  293.     }  
  294. }  

 

代码清单1-2则是我们为引擎实现的一个渲染器,其中我们主要对Opengl视图进行了初始化,大家可以看出,我们留好了3D处理的接口(因为某些2D游戏为了能做得更逼真,同样需要3D的一些效果,当然这也可能需要更高的版本才能支持),这里我们需要给大家特别说明一下设置插值算法及其渲染模式,其中GL_PERSPECTIVE_CORRECTION_HINT是指按照指定颜色和纹理坐标的插值质量。GL_FASTES为使用速度最快的模式GL_NICEST为使用质量最好的模式。由于我们很多时候在做游戏时需要在质量和效率上进行选择,为了保证效率,我们不免会牺牲一些画质,这些将根据具体的游戏来确定;另外我们还开启了抖动(GL_DITHER),因为一旦输入的片元通过了所有的测试,它就可以与颜色缓存中的当前内容按某种方式合并起来。最简单的方法,也是默认操作,就是将当前值覆盖掉。在RGBA模式中,如果希望片元是半透明的或是消除了锯齿现象的,程序员可以将该片元值与缓存中的值作一平均,即混合。对于可用颜色较少的系统,可以以牺牲分辨率为代价,通过颜色值的抖动来增加可用颜色数量。抖动操作是和硬件相关的,OpenGL允许程序员所做的操作就只有打开或关闭抖动操作。实际上,若机器的分辨率已经相当高,激活抖动操作根本就没有任何意义。要激活或取消抖动,可以用glEnable(GL_DITHER)和glDisable(GL_DITHER)函数。默认情况下,抖动是激活的。在颜色索引模式中,可以利用任意的按位逻辑操作,将输入片元与已写入的象素进行合成;最后需要注意的是我们在设置2D投影方式时选择了glOrthof来设置一个正交视口,3D时我们选择了GLU.gluPerspective来实现一个透视投影效果。当然这里目前只有一部分,因为我们所有的事情几乎都于渲染器相关,所以我们后期还会对该类进行完善,包括场景堆栈管理,坐标系转换,中断、暂停等处理。这里我们将先介绍这么多。
 
数据类型
为了方便开发,我们需要对该引擎的数据类型进行统一,该引擎我们将使用如下一些数据类型,下面我们将分别介绍他们的功能及其作用。
 
对象拷贝接口(YFSCopyable)
这里主要是实现一个用于拷贝对象的接口,当然我们需要在子类中实现这个接口来完成对象的拷贝工作,具体实现在每个具体对象中在给大家演示,该接口定义如代码清单1-3所示。
代码清单1-3:YFSCopyable定义
  1. public abstract interface YFSCopyable {  
  2.     public abstract Object copy();  
  3. }  
 
点(YFSPoint)
最基本的数据类型点,定义如代码清单1-4所示。
代码清单1-4:YFSPoint实现
 
  1. public class YFSPoint {  
  2.     public float x;  
  3.     public float y;  
  4.     public static YFSPoint makeZero() {  
  5.        return new YFSPoint(0.0F, 0.0F);  
  6.     }  
  7.     public static YFSPoint makeByRadian(float r) {  
  8.        return make((float) Math.cos(r), (float) Math.sin(r));  
  9.     }  
  10.     public YFSPoint() {  
  11.        this(0.0F, 0.0F);  
  12.     }  
  13.     protected YFSPoint(float x, float y) {  
  14.        this.x = x;  
  15.        this.y = y;  
  16.     }  
  17.     public String toString() {  
  18.        return "(" + this.x + ", " + this.y + ")";  
  19.     }  
  20.     public static boolean isEqual(YFSPoint p1, YFSPoint p2) {  
  21.        return (p1.x == p2.x) && (p1.y == p2.y);  
  22.     }  
  23.     public YFSPoint applyTransform(YFSAffineTransform aTransform) {  
  24.        return aTransform.transform(this);  
  25.     }  
  26.     public static YFSPoint make(float x, float y) {  
  27.        return new YFSPoint(x, y);  
  28.     }  
  29.     public static YFSPoint negate(YFSPoint v) {  
  30.        return make(-v.x, -v.y);  
  31.     }  
  32.     public static YFSPoint add(YFSPoint v1, YFSPoint v2) {  
  33.        return make(v1.x + v2.x, v1.y + v2.y);  
  34.     }  
  35.     public static YFSPoint sub(YFSPoint v1, YFSPoint v2) {  
  36.        return make(v1.x - v2.x, v1.y - v2.y);  
  37.     }  
  38.     public static YFSPoint mul(YFSPoint v, float s) {  
  39.        return make(v.x * s, v.y * s);  
  40.     }  
  41.     public static YFSPoint mul(YFSPoint v, YFSPoint v2) {  
  42.        return make(v.x * v2.x, v.y * v2.y);  
  43.     }  
  44.     public static YFSPoint midpoint(YFSPoint v1, YFSPoint v2) {  
  45.        return mul(add(v1, v2), 0.5F);  
  46.     }  
  47.     public static float dot(YFSPoint v1, YFSPoint v2) {  
  48.        return v1.x * v2.x + v1.y * v2.y;  
  49.     }  
  50.     public static float cross(YFSPoint v1, YFSPoint v2) {  
  51.        return v1.x * v2.y - v1.y * v2.x;  
  52.     }  
  53.     public static YFSPoint perp(YFSPoint v) {  
  54.        return make(-v.y, v.x);  
  55.     }  
  56.     public static YFSPoint rperp(YFSPoint v) {  
  57.        return make(v.y, -v.x);  
  58.     }  
  59.     public static YFSPoint project(YFSPoint v1, YFSPoint v2) {  
  60.        return mul(v2, dot(v1, v2) / dot(v2, v2));  
  61.     }  
  62.     public static YFSPoint rotate(YFSPoint v1, YFSPoint v2) {  
  63.        return make(v1.x * v2.x - v1.y * v2.y, v1.x * v2.y + v1.y * v2.x);  
  64.     }  
  65.     public static YFSPoint rotateByAngle(YFSPoint v, YFSPoint pivot, float angle) {  
  66.        YFSPoint r = sub(v, pivot);  
  67.        float t = r.x;  
  68.        float cosa = (float) Math.cos(angle);  
  69.        float sina = (float) Math.sin(angle);  
  70.        r.x = (t * cosa - r.y * sina);  
  71.        r.y = (t * sina + r.y * cosa);  
  72.        r = add(r, pivot);  
  73.        return r;  
  74.     }  
  75.     public static YFSPoint unrotate(YFSPoint v1, YFSPoint v2) {  
  76.        return make(v1.x * v2.x + v1.y * v2.y, v1.y * v2.x - v1.x * v2.y);  
  77.     }  
  78.     public static YFSPoint lerp(YFSPoint a, YFSPoint b, float alpha) {  
  79.        return add(mul(a, 1.0F - alpha), mul(b, alpha));  
  80.     }  
  81.     public static YFSPoint slerp(YFSPoint a, YFSPoint b, float t) {  
  82.        float omega = (float) Math.acos(dot(a, b));  
  83.    
  84.        if (omega != 0.0F) {  
  85.            float denom = 1.0F / (float) Math.sin(omega);  
  86.            return add(mul(a, (float) Math.sin((1.0F - t) * omega) * denom),  
  87.                   mul(b, (float) Math.sin(t * omega) * denom));  
  88.        }  
  89.        return a;  
  90.     }  
  91.     public static boolean fuzzyEqual(YFSPoint a, YFSPoint b, float var) {  
  92.        return (a.x - var <= b.x) && (b.x <= a.x + var) && (a.y - var <= b.y)  
  93.               && (b.y <= a.y + var);  
  94.     }  
  95.     public static boolean lineIntersect(YFSPoint p1, YFSPoint p2, YFSPoint p3,  
  96.            YFSPoint p4, float[] st) {  
  97.        YFSPoint p13 = sub(p1, p3);  
  98.        YFSPoint p43 = sub(p4, p3);  
  99.    
  100.        YFSPoint zero = makeZero();  
  101.        if (fuzzyEqual(p43, zero, 1.192093E-07F)) {  
  102.            return false;  
  103.        }  
  104.        YFSPoint p21 = sub(p2, p1);  
  105.    
  106.        if (fuzzyEqual(p21, zero, 1.192093E-07F)) {  
  107.            return false;  
  108.        }  
  109.        float d1343 = dot(p13, p43);  
  110.        float d4321 = dot(p43, p21);  
  111.        float d1321 = dot(p13, p21);  
  112.        float d4343 = dot(p43, p43);  
  113.        float d2121 = dot(p21, p21);  
  114.    
  115.        float denom = d2121 * d4343 - d4321 * d4321;  
  116.        if (Math.abs(denom) < 1.192093E-07F)  
  117.            return false;  
  118.        float numer = d1343 * d4321 - d1321 * d4343;  
  119.    
  120.        st[0] = (numer / denom);  
  121.        st[1] = ((d1343 + d4321 * st[0]) / d4343);  
  122.    
  123.        return true;  
  124.     }  
  125.     public static float length(YFSPoint v) {  
  126.        return (float) Math.sqrt(dot(v, v));  
  127.     }  
  128.     public static float lengthsq(YFSPoint v) {  
  129.        return dot(v, v);  
  130.     }  
  131.     public static float distancesq(YFSPoint v1, YFSPoint v2) {  
  132.        return lengthsq(sub(v1, v2));  
  133.     }  
  134.     public static boolean near(YFSPoint v1, YFSPoint v2, float distance){  
  135.        return distancesq(v1, v2) < distance * distance;  
  136.     }  
  137.     public static float distance(YFSPoint v1, YFSPoint v2) {  
  138.        return length(sub(v1, v2));  
  139.     }  
  140.     public static YFSPoint normalize(YFSPoint v) {  
  141.        return mul(v, 1.0F / length(v));  
  142.     }  
  143.     public static YFSPoint toAngle(float a) {  
  144.        return make((float) Math.cos(a), (float) Math.sin(a));  
  145.     }  
  146.     public static float toRadian(YFSPoint v) {  
  147.        return (float) Math.atan2(v.y, v.x);  
  148.     }  
  149. }  
 
看似代码稍微多一点,但是实现都很简单,该类就包含了一个点的x、y坐标,所进行的操作也主要包括了构造,加减乘除、长度、距离、角度等一些列操作,大多是一些数学运算,由于篇幅关系这里我们就不一一介绍了。如果你现在不希望阅读这些代码,那么在使用过程中大家也会明白这些功能。
 
尺寸(YFSSize)
第二个基本的数据就是尺寸,因为我们在游戏开发中很多地方需要使用尺寸,包括问你的尺寸,屏幕的尺寸等,既然是尺寸就主要包括了宽度和高度,定义如代码清单1-5所示。
代码清单1-5:YFSSize实现
  1. public class YFSSize {  
  2.     public float width;  
  3.     public float height;  
  4.     private YFSSize() {  
  5.        this(0.0F, 0.0F);  
  6.     }  
  7.     //...  
  8.     //由于篇幅关系,中间省略了部分重载函数  
  9.     public String toString() {  
  10.        return "<" + this.width + ", " + this.height + ">";  
  11.     }  
  12. }  
 
矩形(YFSRect)
有个点和尺寸,要构成举行就很简单了吧!结构很简单,但是还是需要一部分数学计算,由于代码的确比较多,由于代码的确有些多,后面我们将列出一部分有助于大家理解的即可,完整代码在完善之后,我们会考虑开源。部分代码如代码清单1-6所示。
代码清单1-6:YFSRect实现
  1. public class YFSRect {  
  2.     public YFSPoint origin;  
  3.     public YFSSize size;  
  4.     private static YFSRect sRect = new YFSRect();  
  5.     private static YFSRect rRect = new YFSRect();  
  6.     public static YFSRect makeZero() {  
  7.        return new YFSRect();  
  8.     }  
  9.     public Object copy() {  
  10.        return new YFSRect(this.origin.x, this.origin.y, this.size.width,  
  11.               this.size.height);  
  12.     }  
  13.     //省略部分..  
  14. }  
 
这里我们则实现了一个对象拷贝的函数copy,可以用来复制一个相同的对象副本。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/87503
推荐阅读
相关标签
  

闽ICP备14008679号