android 遊戲導引(3. 圖形引擎之模型管理)

來源:互聯網
上載者:User

android 遊戲導引(3. 圖形引擎之模型管理)

上一節中,我們構建了一個自己的情境世界。可以在內部繪製一些基本圖元了。本來這一節要說說貼圖的,想想還是休息下,放個小插曲,思考下模型的管理,遊戲引擎相關的東西。這些東西跟 cocos2d 很像,可能是 iphone 下常用 cocos2d 的緣故吧, 反正成熟且成功的東西,我們拿來用就行了。

 

源碼下載: 點我吧

 

Table of Contents
  • 1 物件導向吧
  • 2 模型父子鏈:樹
  • 3 層式螢幕管理
  • 4 遊戲引擎
  • 5 代碼實現
  • 6 繪製一個 android 機器人
1 物件導向吧

基於教程到這裡的進度,我們要會議幾個圖元模型的話,就要顯式的在 onDrawFrame 中繪製。基於物件導向的思想,我們知道我們繪製的模型僅僅是一個渲染單位而已,並且我們在 onDrawFrame 的操作只是一個opengl 訪問。 因此基於介面,我們的一個渲染模型類就誕生了。

2 模型父子鏈:樹

你也許會說,上面要渲染的模型太多的時候太散亂了。想到這裡,說明你還是一個合格的程式員,有著對完美的執著。與其手動的管理,不如讓程式自己管理。大家想想,如果是一個複雜的模型,比如一個人,它是有眾多細小的模型構成的,比如腦袋,胳膊,腿等, 而腦袋又有鼻子,耳朵,嘴巴等構成。哈哈,這就是一個樹形的結構了,看下結構特徵:

  • 一個父節點有0個或多個子節點
  • 一個位元組點最多有一個父節點

一個對象的父子關係如下:

現在遊戲多採用樹形模型管理,而且在 GUI 系統中也大放異彩。有了樹形管理,我們可以輕易的將一種類型的鼻子安裝到不同人的腦袋上,不用重複發明輪子了。

好了,現在我們的 onDrawFrame 中的形式是這樣的了,右邊的模型對象是一個封裝好的單位,可能內部許多子節點。

3 層式螢幕管理

我們最終是要把模型繪製到螢幕上,我們思考下面問題:

  1. 螢幕上有眾多模型需要渲染。比如一個人在草原中散步。概括講人模型,還有草原,太陽,河流等等。
  2. 螢幕上的模型又可以歸為不同的類別。如草原,太陽,河流這些是背景,我們控制的人是前景。

於是,層式管理來了,我們的渲染根結點稱為情境 Scene, 分為不同的層 Layer, 在每一層上掛接著具體的模型。看就明白了,(注:來自cocos2d-python 文檔)

為了向渲染 onDrawFrame 突出介面,Scene 和 Layer 也是一個渲染模型單位。

看看我們添加層式螢幕管理後的效果, 我們將所有要繪製的東西添加到一個情境中,就直接對這個情境節點 glVisit() 就可以了:

每個模型突出一個 glVisit(),這個函數的主要功能是調用自身繪製函數 draw() 然後對各個位元組點逐個調用 glVisit().

4 遊戲引擎

一個遊戲引擎的設計遵循的原則是: 邏輯要和渲染分離,盡量將opengl 最小程度封裝在底層,上層使用盡量不要觸及底層api,盡量讓核心代碼原理android系統的api。組件最小集合應該包含下面幾種:

  • 圖形引擎,我們上述討論的就是
  • 事件指派系統,手機應用是基於觸摸事件的,所以獨立出來(事實上,也是一個調度器)
  • 時間調度器,遊戲除了觸摸時間還有自身的時間事件,如動畫

其他的可能用到的組件有碰撞檢測系統,輸入系統,尋路演算法等等。

遊戲引擎的豐富會在以後的教程中逐步增設,今天這一節算是個開頭,弄了個簡單的圖形引擎,也會在接下來隨著需求的需要豐富其功能和介面。為了便於從 android 相關代碼中解脫出來,添加一個 GameSystem 單件類:

 

public class GameSystem {    private static GameSystem instance_ = new GameSystem();    public static GameSystem getInstance()  {       return instance_;   }        // 情境    private GlObject runningScene_= null;    private int width = 0;  // opengl的情境尺寸    private int height = 0;        public void setWindowSize(int width, int height)    {        this.width = width;        this.height = height;    }    public int getWindowWidth() {       return this.width;  }    public int getWindowHeight()    {       return this.height; }        // 設定情境    public void setScene(GlObject scene){       runningScene_ = scene;  }        //      public void glVisit(GL10 gl)    {        if (runningScene_ != null)            runningScene_.glVisit(gl);    }   }

 

我們將其插入到 android 代碼的三個地方:

  1. Activity 的 onCreate 方法中,設定WindowSize: setWindowSize. 設定初始情境 setScene
  2. Renderer 的 onDrawFrame 方法中,來訪問GameSystem 的遊戲情境 glVisit

5 代碼實現

在螢幕中除了上面的介面以外,增加的是座標點,儲存的是相對座標,相對於父節點而言,用於描述模型的位置。這個座標點不要理解為頂點數組中的頂點座標。 可以這樣理解,你將畫筆移動到座標點處,然後在此處依據頂點數組來繪製。代碼較長,省略了一些,可以下載源碼。

 

public class GlObject {    protected GlObject parent = null;       // 父節點    protected LinkedList<GlObject> children = new LinkedList<GlObject>();// 位元組點    private boolean visible = true;     // 可訪問(對 gl)    float x = 0;    float y = 0;            // 父子鏈管理    public void addChild(GlObject obj)  {        children.add(obj);        obj.parent = this;    }    public void removeChild(GlObject obj)   {        obj.parent = null;        children.remove(obj);    }    public void setParent(GlObject p)   {        if (parent != null) parent.removeChild(this);        p.addChild(p);    }    public GlObject getParent() {       return parent;  }        // 座標    float getX()    {       return x;   }    float getY()    {       return y;   }    void setXY(float x, float y)    {        this.x = x;        this.y = y;    }        // visible    public void setVisible(boolean v)   {       visible = true; }    public boolean getVisible() {       return visible; }        // 外部gl提供者    public void glVisit(GL10 gl)    {        if (!visible) return;        gl.glPushMatrix();              gl.glTranslatef(x, y, 0);            this.draw(gl);                            for (GlObject child : children)                child.glVisit(gl);        gl.glPopMatrix();    }            // 繪製自身:子類重寫此方法    public void draw(GL10 gl)   {}        ////////////////////////////////////////////////////////////////////////////////////////     ////  一些常用的圖元繪製    // 繪製正方形    public static void drawQuater(GL10 gl, float left, float top, float right,float bottom) {        // 略    }        // 繪製三角形    public static void drawTriangle(GL10 gl, float oneX, float oneY,float twoX, float twoY, float threeX, float threeY) {        // 略    }        // 繪製扇形    public static void drawArc(GL10 gl, float length, float startAngle,float sweepAngle)    {        // 略    }        // 繪製直線    public static void drawLine(GL10 gl, float oneX, float oneY, float twoX,float twoY) {        // 略    }}

 

6 繪製一個 android 機器人

代碼還是以前的,繪製圖元,只不過經過本章的洗禮,代碼封裝性和擴充性上有了進步。一個機器人有下面的結構組成:

  • 身體
    • 眼睛 * 2
    • 天線
  • 腿 * 2
  • 胳膊 * 2

  1. 設定 GameSystem 的初始遊戲情境:
public class GlGame extends Activity {    /** Called when the activity is first created. */     @Override    public void onCreate(Bundle savedInstanceState) {        // 。。。。略        // 初始化遊戲系統:        // ... 螢幕大小        {            DisplayMetrics dm = new DisplayMetrics();            getWindowManager().getDefaultDisplay().getMetrics(dm);            GameSystem.getInstance().setWindowSize(dm.widthPixels, dm.heightPixels);        }        // ...設定初始情境                GameSystem.getInstance().setScene(new AndroidScene());        //..... setContentView 代碼    }}

    2. 添加一個情境類 AndroidScene 作為我們的畫布,我們將會在其上繪製機器人

    public class AndroidScene extends GlObject{     AndroidRobot robot = new AndroidRobot();        public AndroidScene(){        super();        // 在螢幕中心繪製        robot.setXY(GameSystem.getInstance().getWindowWidth()/2, GameSystem.getInstance().getWindowHeight()/2);        addChild(robot);    }}

        3. 添加一個機器人渲染模型 AndroidRobot, 代碼太長, 摺疊之:

      public class AndroidRobot extends GlObject{    GlColor color = new GlColor(); //一個封裝著 rgba 四元素的簡單類        public AndroidRobot() {        super();                // 顏色為綠色        color.set(0,1,0,0);                //body        // 2 arms        // 2 leg        // head: 2 eyes, 2 line, face        Arm arms[] = new Arm[2];        Leg legs[] = new Leg[2];        Head head;        Body body;                // 構建身體部位        arms[0] = new Arm();        arms[1] = new Arm();        legs[0] = new Leg();        legs[1] = new Leg();        head = new Head();        body = new Body();                // 調整身體組件位置        body.setXY(0, 0); // 身體為中心        head.setXY(0, -65);        arms[0].setXY(-55,0);        arms[1].setXY(55, 0);        legs[0].setXY(-20, 60);        legs[1].setXY(20, 60);                // 安裝身體部位        addChild(body);        addChild(head);        addChild(arms[0]);        addChild(arms[1]);        addChild(legs[0]);        addChild(legs[1]);            }        public void draw(GL10 gl)    {        // 設定顏色為        gl.glColor4f(color.red, color.green, color.blue, color.alpha);    }        // 胳膊    class Arm extends GlObject{        public void draw(GL10 gl)        {            drawQuater(gl, -10, -68, 10, 50);        }    }        // 腿    class Leg extends GlObject{            public void draw(GL10 gl)            {                drawQuater(gl, -15,0,15, 40);            }    }        // 身體    class Body extends GlObject{        public void draw(GL10 gl)        {            drawQuater(gl, -40, -60, 40, 60);        }    }        //頭    class Head extends GlObject{                public Head()        {            Eye eyes[] = new Eye[2];            eyes[0] = new Eye();            eyes[1] = new Eye();                        eyes[0].setXY(-20, -10);            eyes[1].setXY(20, -10);                        addChild(new Face());            addChild(new Antenna());            addChild(eyes[0]);            addChild(eyes[1]);        }                // 眼睛        class Eye extends GlObject{            public void draw(GL10 gl){                //眼睛扣洞                gl.glColor4f(0, 0, 0, 0);                drawQuater(gl, -4, -4, 4,4);                //還原顏色                gl.glColor4f(color.red, color.green, color.blue, color.alpha);            }        }                // 臉        class Face extends GlObject{            public void draw(GL10 gl){                drawArc(gl, 40, 0, 180);                            }        }                // 天線        class Antenna extends GlObject{            public void draw(GL10 gl){                drawLine(gl,0,0, -50,-50);                drawLine(gl, 0, 0, 50, -50);            }        }    }}

      好了,本次就嘮叨到這裡,學習愉快。

      相關文章

      聯繫我們

      該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

      如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

      A Free Trial That Lets You Build Big!

      Start building with 50+ products and up to 12 months usage for Elastic Compute Service

      • Sales Support

        1 on 1 presale consultation

      • After-Sales Support

        24/7 Technical Support 6 Free Tickets per Quarter Faster Response

      • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.