雷霆行动(STG飞机游戏)源码分析
0.前言
本例子取自cping1982早期公开的一个STG源码,loon-simple-20090212,里面带了6个游戏。这次我们要分析的是STGSimple这个飞机游戏。截图如下:
[img]http://dl2.iteye.com/upload/attachment/0104/6933/3503532f-90ca-3860-ac53-634a58f85bf9.png[/img]
出处请参见[url=http://blog.csdn.net/cping1982/article/details/3884313]上半年私人计划简略及Java桌面游戏开发入门示例并源码集合 [/url]
下载地址:[url]http://code.google.com/p/loon-simple/downloads/list[/url]
声明一下,这个程序不是我写的,是cping1982写的。本人在这里斗胆分析一下高手5年前写的代码,一来是提高自己,二来也是给众多小白以信心和勇气,分析完源码你会发现用java写一个飞机或者坦克的游戏还是不难的。
如果你无法访问google code,也可在本文文末下载我已经加上了注释的版本。
下面进入正题。
1. Role 所有角色的基类
这个类是做STG游戏的关键,所有画面上的东东都是“角色”,包括玩家飞机,敌机,老板,子弹等等。于是把他抽象出来。三个方法move,checkHit,draw是关键。
public abstract class Role { protected static GamePanel app; protected Image img; //图片 protected float x; protected float y; protected float WIDTH; protected float HEIGHT; private boolean dead; //是否死亡
//移动 abstract void move();
//碰撞检测 protected boolean checkHit(Role chara) { return x > chara.x - WIDTH && x < chara.x + chara.WIDTH && y > chara.y - HEIGHT && y < chara.y + chara.HEIGHT; }
//把自己画出来 public void draw(Graphics g) { g.drawImage(img, (int) x, (int) y, app); }}
以下是Role类的层次结构图,可以看到,一切东东都是Role的子类。
[img]http://dl2.iteye.com/upload/attachment/0104/6925/19cbb783-e1b9-3650-8470-3c10c41d50a0.png[/img]
Battle是玩家飞机。
Enemy是敌人。
Hero是子弹。
当然类的命名好坏我们就不去评判了,意思到就行了。
2. Battle 玩家飞机
public class Battle extends Role { //tamaIntCount防止子弹放的太快,要限制频率 private int tamaIntCount; //速度(按一次方向键移动的距离) private float speed; //3枚子弹的速度 //第1枚,(-1,-7),x少许向左,y快速向上 //第2枚,(0,-8),x不变,y快速向上 //第3枚,(1,-7),x少许向右,y快速向上 private float tv[] = { -1F, -7F, 0.0F, -8F, 1.0F, -7F }; //当前血量 public int power; //最大血量 public int powerMax;
public void move() { if (Key.left) { //如果按下x键,减速移动,否则全速移动,如果移动超出屏幕,不让移出。上下左右分别重复处理 if (Key.xkey) x -= (double) speed / 4D; else x -= speed; if (x <= 0.0F) x = 0.0F; } //...... //以上是左键处理,其他3个方向类似,此处省略 //......
//如果按下z键,放出3枚子弹,tamaIntCount防止子弹放的太快,要限制频率 if (Key.zkey && tamaIntCount <= 0) { for (int i = 0; i < tv.length; i += 2) { GamePanel.addList(new BattleBasic(x + WIDTH / 2.0F, y, tv[i], tv[i + 1])); tamaIntCount = 8; } } //如果按下x键,放出1枚激光 if (Key.xkey && !Key.zkey && tamaIntCount <= 0) { GamePanel.addList(new BattleBeam(x + WIDTH / 2.0F, y, 0.0F, -8F)); tamaIntCount = 2; } }}
move方法,根据按下的方向键移动。
z,x来生成子弹(BattleBasic)或激光(BattleBeam)。
checkHit判断如果和别的角色碰撞,则扣减血,如果血为0,则死亡
public boolean checkHit(Role chara) { //此处判断并处理与敌机(EnemyA,B,C)或子弹(EnemyShot)碰撞的逻辑 if ((chara instanceof EnemyA) || (chara instanceof EnemyB) || (chara instanceof EnemyC) || (chara instanceof EnemyShot)) { if ((x + WIDTH) - 14F > chara.x && x + 14F < chara.x + chara.WIDTH && (y + HEIGHT) - 12F > chara.y && y + 12F < chara.y + chara.HEIGHT) { chara.dead(); power -= 50; if (power <= 0) { dead(); GamePanel.burst = new Burst(x, y); } return true; } } //...... //此处省略与其他角色碰撞的逻辑,比如boss等 //...... return false; }
3.Hero 子弹
public class Hero extends Role { //xy上的移动速度(带方向) protected float vx; protected float vy;
public void move() { //先移动 x += vx; y += vy; //如果移出屏幕,则死亡,一般是子弹这类 if (x + WIDTH < 0.0F || x > (float) app.getWidth() || y + HEIGHT < 0.0F || y > (float) app.getHeight()) dead(); }}
Hero
|----BattleShot 玩家子弹
| |----BattleBasic 普通子弹
| |----BattleBeam 激光
|----EnemyShot 敌人子弹
|----CircleBullets 圆圈子弹
|----MoveAimingBullet 向玩家发射的子弹
|----......
4. 入口点 Main
public class Main { public static void main(String args[]) { java.awt.EventQueue.invokeLater(new Runnable() { public void run() { new STGFrame(); } }); }}
STGFrame代码省略,就是嵌套一个GamePanel
5.GamePanel (骨架代码)
这个是整个飞机游戏的核心代码。
public class GamePanel extends Panel implements Runnable { //游戏核心线程 private Thread gameThread; //模式 public static int gameMode; //后台Image(目的是缓冲,防画面闪烁) private Image offImage; //后台Graphics private Graphics g_off; //玩家飞机 public static Image heroImage; //所有角色列表 public static LinkedList<Role> list; //临时角色列表 public static LinkedList<Role> listTmp; //玩家 Battle battle;}
5.1 初始化
public GamePanel() { list = new LinkedList<Role>(); listTmp = new LinkedList<Role>(); //载入所有图片 heroImage = Utility.loadImage("image/this.gif"); enemyImageA = Utility.loadImage("image/enemyA.gif"); //...... //此处省略其他图片 //......
gameMode = 0;
//焦点聚焦,接收键盘输入 addKeyListener(new Key()); setFocusable(true); requestFocus();
setBackground(Color.black); setForeground(Color.white);
//启动核心线程 gameThread = new Thread(this); gameThread.start();}
5.2 核心线程
public void run() { while (gameThread == Thread.currentThread()) { //绘制打底图 gameRender(); if (g_off != null) { long RefreshTime = System.currentTimeMillis(); try { Thread.sleep(2L); } catch (InterruptedException e) { } switch (gameMode) { case 0: title(); break;
case 1: stage1(); break;
case 12: ready(); break; //...... //此处省略其他模式 //...... } //前面都是在内存中画,现在一次性画到屏幕上 paintScreen(); //以下防止电脑太快,适当的休息一下(其实是空转cpu,不太好) while (System.currentTimeMillis() - RefreshTime < 13L) ; } }}
可以看到核心线程就是一个死循环,根据不同的模式执行不同的逻辑。
模式表
[table]
|编号|方法|含义|
|0|title|标题画面|
|1|stage1|第1关|
|2|stage2|第2关|
|3|stage3|第3关|
|10|congratulation|恭喜画面|
|11|gameOver|游戏结束画面|
|12|ready|准备阶段|
|13|appearingAnime|玩家飞机登场画面|
|14|crear|打败boss阶段|
|15|disappearing|关卡通关画面|
|16|bossDeathAnime|boss死亡画面|
[/table]
然后关键就是用双缓冲技术画图,防止屏幕闪烁。
private void gameRender() { if (offImage == null) { offImage = createImage(450, 500); if (offImage == null) return; g_off = offImage.getGraphics(); } g_off.setColor(Color.BLACK); g_off.fillRect(0, 0, 450, 500);}public void paintScreen() { try { Graphics g = getGraphics(); if (g != null && offImage != null) g.drawImage(offImage, 0, 0, null); Toolkit.getDefaultToolkit().sync(); if (g != null) g.dispose(); } catch (Exception e) { e.printStackTrace(); }}
5.3 主程序
看一下stage1里面调用主程序gameMain
private void stage1() { StageA.start(); gameMain();}
private void gameMain() { //角色碰撞检测,注意这里很通用,不论玩家飞机还是敌机都存在于这个list中,等待碰撞检测 for (int i = 0; i < list.size(); i++) { Role chara1 = list.get(i); for (int j = 0; j < list.size(); j++) { //2重嵌套循环,判断角色之间的两两碰撞,性能会受影响,可考虑《算法第四版》一书中的碰撞检测算法来优化 Role chara2 = list.get(j); chara1.checkHit(chara2); }
} //移动角色,并绘画 for (int i = list.size() - 1; i >= 0; i--) { Role chara1 = (Role) list.get(i); chara1.move(); chara1.draw(g_off); } //listTmp加入list for (int i = 0; i < listTmp.size(); i++) list.add(listTmp.get(i));
//玩家如果死亡,进入11模式 if (battle.isDead()) { gameMode = 11; } listTmp.clear();}
这里的碰撞检测便是关键了,2重嵌套循环,判断角色之间的两两碰撞。接下来每个角色移动。
可以说它便是许多游戏实现的关键所在。有了这个骨架代码,只要实现不同Role的checkHit和move还有draw方法就可以了,游戏的扩展性变得简单。
6. EnemyTable 敌人登场表
敌人什么时候出现,为了避免硬编码,于是定了一张表格,这个便是表格类。
public class EnemyTable { //登场时间 public int time; //登场xy public float x; public float y; //种类(0,1,2,3,4) public int enemyKind; //形式 public int pattern;}
7. 关卡设计
public class StageA { //依次出现:(时间以50为单位) //2个A,2个A,6个A+1个C static EnemyTable stageA[] = { new EnemyTable(0, 0, 25F, -50F, 0), new EnemyTable(0, 0, 250F, -50F, 1),
new EnemyTable(0, 1, 25F, -50F, 0), new EnemyTable(0, 1, 300F, -50F, 1),
new EnemyTable(0, 2, 30F, -50F, 0), new EnemyTable(0, 2, 20F, -50F, 2), new EnemyTable(0, 2, 100F, -50F, 2), new EnemyTable(0, 2, 200F, -50F, 2), new EnemyTable(0, 2, 300F, -50F, 2), new EnemyTable(0, 2, 330F, -50F, 1), new EnemyTable(2, 2, 20F, -50F, 0), //............ };
public static void start() { for (int i = 0; i < stageA.length; i++) { //如果时间到了(以50为单位),则敌人登场 if ((double) stageA[i].time == (double) GamePanel.time / 50D) if (stageA[i].enemyKind == 0) GamePanel.addList(new EnemyA(stageA[i].x, stageA[i].y, _battle, stageA[i].pattern)); else if (stageA[i].enemyKind == 1) GamePanel.addList(new EnemyB(stageA[i].x, stageA[i].y, _battle, stageA[i].pattern)); //............ }
}
}
看到了吗,这样改关卡就变得容易多了,只要改最上面那个表格就ok了。其实做的更好的话可以提取到一个配置文件里。
8.各种敌人,子弹的设计
接下来就是开动你的想象力的时间了。结合各种数学,物理知识,做出千变万化的角色。
这里分析几个例子
8.1 EnemyA
public class EnemyA extends Enemy { public void move() { //............ } else if (pattern == 2) { //sin函数可以展现弧线的运动轨迹 x += Math.sin((3.1415926535897931D * (double) counter) / 40D) * 5D; y += 1.5D; } else if (pattern == 3) { //转圈 if (counter < 50) y += 2.5D; else if (counter >= 100) { x += Math.sin((3.1415926535897931D * ((double) counter - 100D)) / 160D) * 2.5D; y += Mathh.sin((3.1415926535897931D * ((double) counter - 20D)) / 160D) * 2.5D; } }}
下图分别是pattern 2 和pattern 3的运行轨迹,可以看到pattern2使用一个sin函数可以展现弧线的运动轨迹。而pattern3使用2个sin函数可以让敌机转圈。
[img]http://dl2.iteye.com/upload/attachment/0104/6927/bd0420e6-2bf7-3616-b9a9-131b9e59751b.png[/img]
[img]http://dl2.iteye.com/upload/attachment/0104/6929/eb039297-413e-31b2-abfa-092b1b55df57.png[/img]
8.2 MoveAimingBullet
这个类不错,敌人会朝着玩家发射出子弹,而不是乱放空枪,使得敌人少许有了一些AI。
public MoveAimingBullet(float x, float y, Battle ziki) { super(x, y); speed = 2.0F; x_ziki = ziki.x; y_ziki = ziki.y; x_enemy = x; y_enemy = y; distance = (float) Math .sqrt((((double) x_ziki + (double) ziki.WIDTH / 2D) - (double) x_enemy) * (((double) x_ziki + (double) ziki.WIDTH / 2D) - (double) x_enemy) + (double) ((y_ziki - y_enemy) * (y_ziki - y_enemy))); if (distance != 0.0F) { vx = (float) (((((double) x_ziki + (double) ziki.WIDTH / 2D) - (double) x_enemy) / (double) distance) * (double) speed); vy = ((y_ziki - y_enemy) / distance) * speed; } else { vx = 0.0F; vy = speed; }}
[img]http://dl2.iteye.com/upload/attachment/0104/6931/c1b2af7a-cd08-3027-8bea-cb0c15e59271.png[/img]
如图,数学公式就是先算出敌我之间的距离,distance=sqrt(x^2+y^2)
然后放出的子弹的速度就是
vx=x/distance
vy=y/distance
8.3 CircleBullets
老版更厉害,可以放出一个圆圈的子弹。
public CircleBullets(float x, float y, boolean flag) { super(x, y); speed = 1.0F; //老版一次放24枚子弹 tamaNum = 24; vxCircle = new float[tamaNum]; vyCircle = new float[tamaNum]; float rad_step = (float) (6.2831853071795862D / (double) tamaNum); float rad; if (flag) rad = 0.0F; else rad = (float) ((double) rad_step / 2D); for (int i = 0; i < tamaNum;) { vxCircle[i] = (float) (Math.cos(rad) * (double) speed); vyCircle[i] = (float) (Math.sin(rad) * (double) speed); GamePanel.addList(new EnemyShot(x, y, vxCircle[i], vyCircle[i])); i++; rad += rad_step; }
vx = vxCircle[0]; vy = vyCircle[0];}
[img]http://dl2.iteye.com/upload/attachment/0104/6933/3503532f-90ca-3860-ac53-634a58f85bf9.png[/img]
9.总结
可以看到,用java做游戏并不难。这个例子可以作为新手做STG游戏(飞机大战,坦克大战)的一个起步。
当然这个例子是纯java,没有用任何框架。当代码膨胀了以后,可以考虑用一些游戏框架来解放我们的生产力。
另外,[url=http://blog.csdn.net/cping1982]原作者cping1982的博客[/url]相当好,有许多java游戏编程的资料值得参考。
雷霆行动(STG飞机游戏)源码分析相关推荐
- 【181007】VC++ 打飞机游戏源码
VC++ 打飞机游戏,一款VC++游戏源码,没搞懂怎么玩,因为看不出飞机在哪里飞!游戏可以设置,自己布局游戏,带声音,玩起来挺有意思.在VC++6.0下编译通过,运行如下示. 源码下载地址:点击下载 ...
- HTML基于蔡徐坤的打飞机游戏源码
正文: 坤坤一直都是非常火的,所以坤坤的真爱粉就开发出了这个游戏源码,有兴趣的自行去体验了. 程序: wwegr.lanzouw.com/iPUKM07wpakf 图片:
- 《游戏学习》| 微信蜘蛛侠动作游戏源码分析
整个游戏源码是由html.js. css.图片等代码完成的,无后端数据保存功能. 代码结构 js文件夹是游戏事件控制文件 vapp文件夹是游戏图片文件 icon.png 是网页游戏图标 index.h ...
- 《游戏学习》| 水果忍者HTML5网页版在线游戏 | 源码分析
游戏介绍 这是一款由百度JS小组提供的HTML5版切水果游戏,这款基于HTML5实现的网页版切水果游戏虽然和原版的切水果游戏相比仍有美中不足之处,但也算有声有色,画面效果也十分炫目华丽. 游戏截图 主 ...
- Java华容道小游戏源码分析
文章目录 1 界面分析 2 启动类 3 人物类 3.1 构造方法 3.2 人物变色监听 3.3 人物类完整代码 4 游戏窗体类 4.1 构造方法 4.2 初始化布置 4.3 人物移动操作 4.4 游戏 ...
- java 飞机 源码_Java 飞机游戏源码(带音乐)
[实例简介] [实例截图] [核心代码] package liujunbo.feiji; import java.awt.Color; import java.awt.Frame; import ja ...
- 飞机大战HTML5游戏源码,基于Canvas制作的网页版飞机大战游戏+飞机大战手机端
简介: 飞机大战HTML5游戏源码是一款基于Canvas制作的网页版飞机大战游戏,画质精美的飞机大战手机端游戏源码 网盘下载地址: http://kekewangLuo.net/W1S2LQcqAT2 ...
- Unity3D RPG角色扮演游戏源码(上下)-----源代码分析----01-----主角人物动画
在源代码的里面有fbx格式的模型文件,发现有2个文件,一个是骨骼动画文件,可以分割为多个动画片段,还有一个是模型文件,但是没有动画,但是可以导入分割好的动画片段到动画元素里面, 按照下面的说明,分割了 ...
- UnityStandardAsset工程、源码分析_5_赛车游戏[AI控制]_AI机制
上一章地址: UnityStandardAsset工程.源码分析_4_赛车游戏[玩家控制]_摄像机控制 前几章我们已经将赛车游戏的绝大多数机制分析过了,而Unity还提供了不同的操控模式--AI控制. ...
最新文章
- mysql教程多表查询_mysql重点,表查询操作和多表查询
- 在QT搭建的播放器外壳中嵌入SDL的窗口
- 网站性能测试指标(QPS,TPS,吞吐量,响应时间)详解
- 计算机办公自动化考题,办公自动化试题(附答案).doc
- 技术干货 | 阿里云数据库PostgreSQL 13大版本揭秘
- 云图说|OLAP开源引擎的一匹黑马,MRS集群组件之ClickHouse
- 10-Bootstrap Checksedit
- 遂冀机器人_409支队伍1006名选手角逐第12届河北省青少年机器人竞赛
- python语言适合哪些领域的计算问题数据处理和文本挖掘_R和Python中文本挖掘8大入门指南...
- linux iptables 防火墙
- 百度地图集成Plist文件需要增加的字段
- SwiftyJSON源码分析
- 投资理财书籍推荐 理财书籍排行榜前十
- bootbox 使用方式
- 对数(log)的换算公式
- php 搜索引擎 分词_怎么在php中使用scws实现一个中文分词搜索功能
- Frosty the snowman(圣诞英文歌曲)铃声 Frosty the snowman...
- 报计算机用几寸照片,在电脑显示的2寸照片的大小是几×几?
- E. Thematic Contests【dp】
- 第一节 模式识别的基本概念
热门文章
- 13-ZF标志位,JB和JNB跳转指令
- Win7笔记本电脑启用虚拟WIFI共享上网
- L1 - Learn 8 Phrasal Verbs for opening: pop open, peel off, flip up…
- Google - Colab实验室 - 柯基犬猫咪
- PicGo配置(gitee)
- 双栈完全解决计算器问题
- php ffmpeg 转码mp4,PHP+FFMPEG实现将视频自动转码成H264标准Mp4文件
- [Unity实践笔记] 俯视视角人物360°移动脚本
- 密钥远程登录与scp
- Excel给证件照换底色,红、蓝、白底任你换,看完涨知识了