2012年7月11日 星期三

【Android遊戲開發之四】Android 遊戲框架(一個遊戲角色在螢幕行走的demo)

~ 各位童鞋請你們注意:surfaceview中確實有 onDraw這個方法,但是surfaceview不會自己去調用!!!

    而我代碼中的ondraw 也好 draw 也好,都是我自己定義的一個方法。。。放在執行緒中不斷調用的,一定要注意!!

    其實上一篇分析surfaceview的文章就是一個簡單的遊戲框架了,當然這裡再強調一下,簡單的遊戲框架,所以不要 高手們不要亂噴~

     這個Demo是給一童鞋寫的一個對圖片操作以及按鍵處理,遊戲簡單框架的一個demo,這裡放出給大家分享~
  1. package com.himi;
  2. import android.content.Context;
  3. import android.content.res.Resources;
  4. import android.graphics.Bitmap;
  5. import android.graphics.BitmapFactory;
  6. import android.graphics.Canvas;
  7. import android.graphics.Color;
  8. import android.graphics.Paint;
  9. import android.util.Log;
  10. import android.view.KeyEvent;
  11. import android.view.SurfaceHolder;
  12. import android.view.SurfaceView;
  13. import android.view.SurfaceHolder.Callback;
  14. public class MySurfaceView extends SurfaceView implements Callback, Runnable {
  15. private Thread th = new Thread(this);
  16. private SurfaceHolder sfh;
  17. private int SH, SW;
  18. private Canvas canvas;
  19. private Paint p;
  20. private Paint p2;
  21. private Resources res;
  22. private Bitmap bmp;
  23. private int bmp_x = 100, bmp_y = 100;
  24. private boolean UP, DOWN, LEFT, RIGHT;
  25. private int animation_up[] = { 3, 4, 5 };
  26. private int animation_down[] = { 0, 1, 2 };
  27. private int animation_left[] = { 6, 7, 8 };
  28. private int animation_right[] = { 9, 10, 11 };
  29. private int animation_init[] = animation_down;
  30. private int frame_count;
  31. public MySurfaceView(Context context) {
  32. super(context);
  33. this.setKeepScreenOn(true);
  34. res = this.getResources();
  35. bmp = BitmapFactory.decodeResource(res, R.drawable.enemy1);
  36. sfh = this.getHolder();
  37. sfh.addCallback(this);
  38. p = new Paint();
  39. p.setColor(Color.YELLOW);
  40. p2 = new Paint();
  41. p2.setColor(Color.RED);
  42. p.setAntiAlias(true);
  43. setFocusable(true); //备注1
  44. }
  45. public void surfaceCreated(SurfaceHolder holder) {
  46. SH = this.getHeight();
  47. SW = this.getWidth();
  48. th.start();
  49. }
  50. public void draw() {
  51. canvas = sfh.lockCanvas();
  52. canvas.drawRect(0, 0, SW, SH, p); //备注2
  53. canvas.save(); //备注3
  54. canvas.drawText("Himi", bmp_x-2, bmp_y-10, p2);
  55. canvas.clipRect(bmp_x, bmp_y, bmp_x + bmp.getWidth() / 13, bmp_y+bmp.getHeight());
  56. if (animation_init == animation_up) {
  57. canvas.drawBitmap(bmp, bmp_x - animation_up[frame_count] * (bmp.getWidth() / 13), bmp_y, p);
  58. } else if (animation_init == animation_down) {
  59. canvas.drawBitmap(bmp, bmp_x - animation_down[frame_count] * (bmp.getWidth() / 13), bmp_y, p);
  60. } else if (animation_init == animation_left) {
  61. canvas.drawBitmap(bmp, bmp_x - animation_left[frame_count] * (bmp.getWidth() / 13), bmp_y, p);
  62. } else if (animation_init == animation_right) {
  63. canvas.drawBitmap(bmp, bmp_x - animation_right[frame_count] * (bmp.getWidth() / 13), bmp_y, p);
  64. }
  65. canvas.restore(); //备注3
  66. sfh.unlockCanvasAndPost(canvas);
  67. }
  68. public void cycle() {
  69. if (DOWN) {
  70. bmp_y += 5;
  71. } else if (UP) {
  72. bmp_y -= 5;
  73. } else if (LEFT) {
  74. bmp_x -= 5;
  75. } else if (RIGHT) {
  76. bmp_x += 5;
  77. }
  78. if (DOWN || UP || LEFT || RIGHT) {
  79. if (frame_count < 2) {
  80. frame_count++;
  81. } else {
  82. frame_count = 0;
  83. }
  84. }
  85. if (DOWN == false && UP == false && LEFT == false && RIGHT == false) {
  86. frame_count = 0;
  87. }
  88. }
  89. @Override
  90. public boolean onKeyDown(int key, KeyEvent event) {
  91. if (key == KeyEvent.KEYCODE_DPAD_UP) {
  92. if (UP == false) {
  93. animation_init = animation_up;
  94. }
  95. UP = true;
  96. } else if (key == KeyEvent.KEYCODE_DPAD_DOWN) {
  97. if (DOWN == false) {
  98. animation_init = animation_down;
  99. }
  100. DOWN = true;
  101. } else if (key == KeyEvent.KEYCODE_DPAD_LEFT) {
  102. if (LEFT == false) {
  103. animation_init = animation_left;
  104. }
  105. LEFT = true;
  106. } else if (key == KeyEvent.KEYCODE_DPAD_RIGHT) {
  107. if (RIGHT == false) {
  108. animation_init = animation_right;
  109. }
  110. RIGHT = true;
  111. }
  112. return super.onKeyDown(key, event);
  113. }
  114. /* (non-Javadoc)
  115. * @see android.view.View#onKeyUp(int, android.view.KeyEvent)
  116. */
  117. @Override
  118. public boolean onKeyUp(int keyCode, KeyEvent event) {
  119. if (DOWN) {
  120. DOWN = false;
  121. } else if (UP) {
  122. UP = false;
  123. } else if (LEFT) {
  124. LEFT = false;
  125. } else if (RIGHT) {
  126. RIGHT = false;
  127. }
  128. return super.onKeyUp(keyCode, event);
  129. }
  130. @Override
  131. public void run() {
  132. // TODO Auto-generated method stub
  133. while (true) {
  134. draw();
  135. cycle();
  136. try {
  137. Thread.sleep(100);
  138. } catch (Exception ex) {
  139. }
  140. }
  141. }
  142. @Override
  143. public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
  144. // TODO Auto-generated method stub
  145. }
  146. @Override
  147. public void surfaceDestroyed(SurfaceHolder holder) {
  148. // TODO Auto-generated method stub
  149. }
  150. }
備註1

此方法是用來回應按鍵!如果是自己定義一個繼承自View的類,重新實現onKeyDown方法後,只有當該View獲得焦點時才會調用onKeyDown方法,Actvity中的onKeyDown方法是當所有控制項均沒有處理該按鍵事件時,才會調用.

備註2

這裡也是對螢幕進行刷屏操作,其實這也只是一種,之前文章裡我也用到drawRGB的方法同樣實現,當然也可以用fillRect等來刷屏。

那麼這裡我想說下,在繼承view中,因為onDraw方法是系統自動調用的,不像在surfaceview這裡這樣去在run裡面自己去不斷調用,在view中我們可以抵用 invalidate()/postInvalidate() 這兩種方法實現讓系統調用onDraw方法,這裡也是和surfaceview中的不同之一!

備註3

這裡canvas.save();和canvas.restore();是兩個相互匹配出現的,作用是用來保存畫布的狀態和取出保存的狀態的。這裡稍微解釋一下,當我們對畫布進行旋轉,縮放,平移等操作的時候其實我們是想對特定的元素進行操作,比如圖片,一個矩形等,但是當你用canvas的方法來進行這些操作的時候,其實是對整個畫布進行了操作,那麼之後在畫布上的元素都會受到影響,所以我們在操作之前調用canvas.save()來保存畫布當前的狀態,當操作之後取出之前保存過的狀態,這樣就不會對其他的元素進行影響。

對於 canvas.save();和canvas.restore(); 還有不少童鞋不懂,OK、我再補充點:

程式碼片段1:
 
  1. public void draw() {
  2. Canvas canvas = sfh.lockCanvas();
  3. canvas.drawColor(Color.BLACK);
  4. canvas.drawBitmap(bmp1, 0,0,paint);
  5. canvas.save();
  6. canvas.scale(1.5f, 1.5f);
  7. canvas.restore();
  8. canvas.drawBitmap(bmp2, 0,0,paint);
  9. sfh.unlockCanvasAndPost(canvas);
  10. }
程式碼片段2:
  1. public void draw() {
  2. Canvas canvas = sfh.lockCanvas();
  3. canvas.drawColor(Color.BLACK);
  4. canvas.drawBitmap(bmp1, 0,0,paint);
  5. canvas.scale(1.5f, 1.5f);
  6. canvas.drawBitmap(bmp2, 0,0,paint);
  7. sfh.unlockCanvasAndPost(canvas);
  8. }
上面這兩個代碼片段中我們都假設有兩張圖片 bmp1和bmp2,並且都畫在畫布上!

那麼程式碼片段1和程式碼片段2的不同:

程式碼片段1中我們進行畫布縮放的之前保存了畫布狀態,做了縮放操作之後又取出之前保存的狀態,這樣做是為了保證bmp2正常畫出來不受到縮放的影響!

程式碼片段2裡,畫了bmp1後就執行了縮放操作,並且沒有保存狀態!緊接著畫了bmp2,那麼bmp2也會一樣受到縮放的影響!!

所以我們如果單獨處理一張圖片的時候,而且不想影響其他部分的繪製,那麼應該如下來做:
  1. public void draw() {
  2. Canvas canvas = sfh.lockCanvas();
  3. canvas.drawColor(Color.BLACK);
  4. canvas.drawBitmap(bmp1, 0,0,paint);
  5. canvas.save();
  6. canvas.scale(1.5f, 1.5f);
  7. canvas.drawBitmap(bmp2, 0,0,paint);
  8. canvas.restore();
  9. sfh.unlockCanvasAndPost(canvas);
  10. }

沒有留言:

張貼留言