如何构建自己的游戏框架并且制作游戏(一)(附源码)
这个教程就让我们学习怎么用这个游戏框架开发一个简单的空战游戏吧!由于素材有限,都是用的网上的素材。这个游戏可以改造成为空战或者植物大战僵尸等的养成类型游戏或者更多,原理都差不多。 一个出类拔萃的人总是一个有耐心的人! 一个游戏的制作经常会出现小意外,一个不耐心的人往往会不知所措,我看过李华明他的书上面有介绍游戏框架,而且很详细,但是没有这个全面,现在的很多游戏书籍也很少有关于游戏框架的构建,希望大家可以多借鉴一下,多提提意见!
先上图:
第一个教程就先搭建属于我们的游戏框架:
com.mocn.framework中是框架包
com.mocn.airBottle中是游戏包
首先看框架包 中的BaseActivity类,主要用于设置横竖屏,全屏,屏幕的宽高度等等。
package com.mocn.framework; import android.R; import android.app.Activity; import android.content.pm.ActivityInfo; import android.os.Bundle; import android.util.DisplayMetrics; import android.view.Window; import android.view.WindowManager; /** * Activity的基类,本类中只要设置屏幕为竖屏,全屏,得到屏幕的高度和宽度 * * @author Administrator * */ public class BaseActivity extends Activity { /** * Activity创建时执行的方法 */ @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setFullScreen(); // 设置全屏 setLandscape(); // 设置横屏 Global.context = this ; // 获取上下文事件 // 获取屏幕的宽高 DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); Global.screenWidth = dm.widthPixels; // 获取屏幕的宽度 Global.screenHeight = dm.heightPixels; // 获取屏幕的高度 } /** * 设置为全屏的方法 */ public void setFullScreen() { requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); } /** * 设置为竖屏的方法 */ public void setPortrait() { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } /** * 设置为横屏的方法 */ public void setLandscape() { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } } |
BaseView,主要用于设置线程的开关,游戏界面的绘制
package com.mocn.framework; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.view.SurfaceHolder; import android.view.SurfaceHolder.Callback; import android.view.SurfaceView; /** * 游戏界面的基类 SurfaceHolder,线程的开启和关闭,添加組件drawSurfaceView * * @author Administrator * */ public abstract class BaseView extends SurfaceView implements Callback, Runnable { private SurfaceHolder holder; // SurfaceHolder的引用 private Thread currentThread; // Thread的引用 public int sleepTime = 20 ; // 设置睡眠时间 public BaseView(Context context) { super (context); holder = this .getHolder(); // 得到holder对象 holder.addCallback( this ); // 得到调函数对象 } /** * 开启线程的方法 */ public void startThread() { currentThread = new Thread( this ); // 得到线程的对象 currentThread.start(); // 开启线程 } /** * 关闭线程的方法 */ public void stopThread() { currentThread = null ; // 设置线程为空 } /** * 当界面改变时执行的方法 */ @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } /** * 当界面创建时执行的方法 */ @Override public void surfaceCreated(SurfaceHolder holder) { startThread(); // 开启游戏线程 } /** * 当游戏被摧毁时执行的方法 */ @Override public void surfaceDestroyed(SurfaceHolder holder) { stopThread(); // 关闭游戏线程 } /** * 绘制界面的方法 * * @param canvas * @param paint */ public void drawSurfaceView(Canvas canvas, Paint paint) { LayerManager.drawLayerManager(canvas, paint); // 绘制组件 } /** * 线程的控制方法 */ @Override public void run() { Canvas canvas; Paint paint = new Paint(); while (currentThread != null ) { canvas = holder.lockCanvas(); drawSurfaceView(canvas, paint); holder.unlockCanvasAndPost(canvas); try { Thread.sleep(sleepTime); } catch (InterruptedException e) { e.printStackTrace(); } } } } |
Global类,用于设置一些常量
package com.mocn.framework; import android.content.Context; /** * 得到屏幕的宽度,高度,Context对象 * * @author Administrator * */ public class Global { public static Context context; //得到上下文的引用 public static int screenWidth; //屏幕的宽度 public static int screenHeight; //屏幕的高度 } |
Layer类,所以绘制组件的基类,里面包括组件的坐标,宽高,以及绘制的方法等
package com.mocn.framework; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; /** * 层类,组件的父类,添加组件,设置组件位置,绘制自己, 是所有人物和背景的基类 * * @author Administrator * */ public abstract class Layer { public float x; // 层的x坐标 public float y; // 层的y坐标 public int w; // 层的宽度 public int h; // 层的高度 public Rect src, dst; // 引用Rect类 public Bitmap bitmap; // 引用Bitmap类 protected Layer(Bitmap bitmap, int w, int h, boolean autoAdd) { this .bitmap = bitmap; this .w = w; this .h = h; src = new Rect(); dst = new Rect(); if (autoAdd) { LayerManager.addLayer( this ); // 在LayerManager类中添加本组件 } } /** * 设置组件位置的方法 * * @param x * @param y */ public void setPosition( float x, float y) { this .x = x; this .y = y; } /** * 绘制自己的抽象接口 * * @param canvas * @param paint */ public abstract void drawSelf(Canvas canvas, Paint paint); } |
BackGroundLayer类,主要用于背景的绘制,可以用做静态的绘制
package com.mocn.framework; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; /** * 游戏背景组件,在LayerManager中添加游戏背景组件,绘制自己 * * @author Administrator * */ public class BackGroundLayer extends Layer { public BackGroundLayer(Bitmap bitmap, int w, int h) { super (bitmap, w, h, true ); } @Override public void drawSelf(Canvas canvas, Paint paint) { src.left = 0 ; src.top = 0 ; src.right = w; src.bottom = h; dst.left = ( int ) x; dst.top = ( int ) y; dst.right = dst.left + w; dst.bottom = dst.top + h; canvas.drawBitmap(bitmap, src, dst, paint); } } |
Sprite类,精灵类,用于绘制动态人物
package com.mocn.framework; import java.util.Hashtable; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; /** * 精灵组件类,添加精灵组件,设置动作,绘制精灵,添加一个动作序列,精灵动作类 * * @author Administrator * */ public class Sprite extends Layer { public int frameIdx; // 当前帧下标 public int currentFrame = 0 ; // 当前帧 public Hashtable<String, SpriteAction> actions; // 动作集合 public SpriteAction currentAction; // 当前动作 public Sprite(Bitmap bitmap, int w, int h, boolean autoAdd) { super (bitmap, w, h, autoAdd); actions = new Hashtable<String, Sprite.SpriteAction>(); // 用Hashtable保存动作集合 } /** * 设置动作的方法 * * @param actionName */ public void setAction(String actionName) { currentAction = actions.get(actionName); // 从动作集合中得到该动作 } /** * 绘制精灵的方法 */ @Override public void drawSelf(Canvas canvas, Paint paint) { if (currentAction != null ) { currentFrame = currentAction.frames[frameIdx]; // 获取当前需要的帧 } // 截取图片中需要的帧 src.left = currentFrame * w; // 左端宽度:当前帧乘上帧的宽度 src.top = 0 ; // 上端高度:0 src.right = src.left + w; // 右端宽度:左端宽度加上帧的宽度 src.bottom = h; // 下端高度为帧的高度 // 绘制在界面上,取中心点绘制 dst.left = ( int ) x - w / 2 ; dst.top = ( int ) y - h / 2 ; dst.right = dst.left + w; dst.bottom = dst.top + h; canvas.drawBitmap(bitmap, src, dst, paint); // 绘制当前帧 if (currentAction != null ) { currentAction.nextFrame(); // 绘制下一帧 } } /** * 添加一个动作集合的方法 * * @param name * @param frames * @param frameTime */ public void addAction(String name, int [] frames, int [] frameTime) { SpriteAction sp = new SpriteAction(); // 创建SpiteAction的对象 sp.frames = frames; // 当前帧的数量,用下标表示 sp.frameTime = frameTime; // 每一帧切换的时间 actions.put(name, sp); // 将名字为"name"的动作集合,值为sp的动作集合放进acitons中 } /** * 精灵的移动方法 * * @param dx * @param dy */ public void move( int dx, int dy) { this .x += dx; this .y += dy; } // 精灵动作类 class SpriteAction { public int [] frames; // 该动作的帧序列 public int [] frameTime; // 帧序列中每一帧切换对应的时间 private long updateTime; // 记录上次失效时间 /** * 切换到下一帧的方法 */ public void nextFrame() { if (System.currentTimeMillis() > updateTime) { frameIdx++; // 帧下标增加 frameIdx %= frames.length; updateTime = System.currentTimeMillis() + frameTime[frameIdx]; // 切换下一帧所需要的时间 } } } } |
LayerManager类,组件管理类,用于管理组件
package com.mocn.framework; import java.util.Vector; import android.graphics.Canvas; import android.graphics.Paint; /** * 组件的管理类,用于存放组件,绘制所有组件,添加一个组件,删除一个组件,插入一组件 * * * @author Administrator * */ public class LayerManager { public static Vector<Layer> vec = new Vector<Layer>(); // Vector对象用于存放所有组件 /** * 绘制所有组件的方法 * * @param canvas * @param paint */ public static void drawLayerManager(Canvas canvas, Paint paint) { for ( int i = 0 ; i < vec.size(); i++) { vec.elementAt(i).drawSelf(canvas, paint); // 把存在于Vector对象中的组件绘制出来 } } /** * 添加一个组件的方法 * * @param layer */ public static synchronized void addLayer(Layer layer) { vec.add(layer); // 在Vector对象中添加此组件 } /** * 删除一个组件的方法 * * @param layer */ public static synchronized void deleteLayer(Layer layer) { vec.remove(layer); // 在Vector对象中删除此组件 } /** * 在before指定的位置插入layer,原来对象以及此后的对象依次往后顺延。 * * @param layer * @param before */ public static void insert(Layer layer, Layer before) { for ( int i = 0 ; i < vec.size(); i++) { // 遍历Vector对象 if (before == vec.elementAt(i)) { vec.insertElementAt(layer, i); // 在before对象前面插入layer,该对象位于before之上 return ; } } } } |
最后一个,Utilsl类,工具类,包含各种取得图片,碰撞事件的检测等方法
package com.mocn.framework; import java.io.IOException; import java.io.InputStream; import android.graphics.Bitmap; import android.graphics.BitmapFactory; /** * 工具类 获得一张图片,判断两个矩形是否相交,判断一个点是否在矩形内 * * @author Administrator * */ public class Utils { /** * 获取一张图片的方法 * * @param path * @return */ public static Bitmap getBitmap(String path) { try { InputStream is = Global.context.getAssets().open(path); return BitmapFactory.decodeStream(is); } catch (IOException e) { e.printStackTrace(); } return null ; } /** * 判断两个矩形是否相交的方法 * * @param x * @param y * @param w * @param h * @param x2 * @param y2 * @param w2 * @param h2 * @return */ public static boolean colliseWidth( float x, float y, float w, float h, float x2, float y2, float w2, float h2) { if (x > x2 + w2 || x2 > x + w || y > y2 + h2 || y2 > y + h) { return false ; } return true ; } /** * 判断 一个点是否在矩形内的方法 * * @param x * @param y * @param w * @param h * @param px * @param py * @return */ public static boolean inRect( float x, float y, float w, float h, float px, float py) { if (px > x && px < x + w && py > y && py < y + h) { return true ; } return false ; } } |
框架搭建完成,第二篇就是游戏的绘制篇了,如果大家在框架上有什么问题可以问我。
下面附上源码:
https://files.cnblogs.com/feifei1010/AirBottle.zip
还有看到好多优秀的帖子http://www.apkbus.com/blog-15060-40268.html
欢迎热爱安卓开发的朋友么加入群一起讨论,进步!!成都群252743807 武汉群121592153
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!