`
xpenxpen
  • 浏览: 724983 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

雷霆行动(STG飞机游戏)源码分析

阅读更多
0.前言
本例子取自cping1982早期公开的一个STG源码,loon-simple-20090212,里面带了6个游戏。这次我们要分析的是STGSimple这个飞机游戏。截图如下:


出处请参见上半年私人计划简略及Java桌面游戏开发入门示例并源码集合
下载地址:http://code.google.com/p/loon-simple/downloads/list

声明一下,这个程序不是我写的,是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的子类。

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)
				;
		}
	}
}



可以看到核心线程就是一个死循环,根据不同的模式执行不同的逻辑。
模式表
编号方法含义
0title标题画面
1stage1第1关
2stage2第2关
3stage3第3关
10congratulation恭喜画面
11gameOver游戏结束画面
12ready准备阶段
13appearingAnime玩家飞机登场画面
14crear打败boss阶段
15disappearing关卡通关画面
16bossDeathAnimeboss死亡画面


然后关键就是用双缓冲技术画图,防止屏幕闪烁。
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函数可以让敌机转圈。



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;
	}
}



如图,数学公式就是先算出敌我之间的距离,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];
}




9.总结
可以看到,用java做游戏并不难。这个例子可以作为新手做STG游戏(飞机大战,坦克大战)的一个起步。
当然这个例子是纯java,没有用任何框架。当代码膨胀了以后,可以考虑用一些游戏框架来解放我们的生产力。
另外,原作者cping1982的博客相当好,有许多java游戏编程的资料值得参考。
  • 大小: 19.4 KB
  • 大小: 44.1 KB
  • 大小: 43.9 KB
  • 大小: 49.5 KB
  • 大小: 46.4 KB
分享到:
评论

相关推荐

    Java仿雷电(雷霆行动)及其源代码

    《Java仿雷电(雷霆行动)及其源代码》是一款基于Java编程语言开发的STG(Shoot Them Up,射击类)游戏,旨在模仿经典的雷电系列。这款游戏由GreenJVM发布,一个专门针对Java应用程序优化的运行环境,允许游戏在没有...

    STG_File.rar_stg_stg 格式_stg文件_stg格式

    4. 游戏存档:游戏可能使用STG文件保存进度和用户数据。 总的来说,STG文件是信息技术领域中一个重要的数据存储解决方案,其灵活性和适应性使其在各种场景下都有所应用。理解和掌握如何操作STG文件,对于提升工作...

    STG 检测路由器的流量

    标题中的“STG检测路由器流量”指的是一个专门用于监控路由器网络流量的工具或软件,它采用了SNMP(Simple Network Management Protocol)协议来收集和分析数据。SNMP是一种广泛应用于网络设备管理的标准协议,允许...

    STG:跨平台STG游戏引擎

    STG(Shoot Them Up)是一种深受玩家喜爱的游戏类型,通常涉及空中战斗和射击元素。而“跨平台STG游戏引擎”则是专为开发这类游戏设计的软件工具,它允许开发者在多个操作系统上构建和运行STG游戏,如Windows、MacOS...

    STG (SNMP Traffic Grapher)

    STG (SNMP Traffic Grapher) version 1.4.5 Copyright (C) 2000 Leonid Mikhailov This freeware utility allows monitoring of supporting SNMPv1 and SNMPv2c devices including Cisco, Livingstone, River...

    uC_OC_II.rar_OC STG_uc oc源码

    在“uC_OC_II.rar_OC STG_uc oc源码”这个压缩包中,包含了uC/OS-II的源代码,这对于开发者来说是一份极其宝贵的资源,能够深入理解其工作原理并进行定制化开发。 1. **uC/OS-II核心概念** - **任务(Task)**:uC...

    基于Java语言开发的socket_server_stg游戏后台服务端设计源码

    该项目为Java语言编写的游戏后台服务端设计源码,包含48个文件,其中包括32个Java源文件、7个属性文件、3个偏好设置文件以及少量其他类型文件。该系统专注于提供高效稳定的服务端支持,适用于游戏开发场景。

    高分项目,基于Unity3D开发实现的官方超好画质的射击游戏源码,内含完整源码+资源+unitypackage

    高分项目,基于Unity3D开发实现的官方超好画质的射击游戏源码,内含完整源码+资源+unitypackage 射击游戏(Shooting game),简称为STG。游戏类型的一种,也是动作游戏的一种。射击游戏带有很明显的动作游戏特点,也没有...

    (Flash源码) 游戏L.A.2 - 几何战争作者开发的STG游戏

    一个由“几何战争”作者开发的STG游戏,躲避子弹然后按Z反击:) 压缩包中包括源码和编译好的swf文件,自己试试吧! (上下左右控制方向,按着Z不放是向前方发出子弹) Control your ship Arrow keys Fire a glider ...

    stg719芯片资料

    STG719是一款高速S.P.D.T. (单刀双掷)SWITCH采用硅栅C2MOS技术制造。 它的设计工作电压为1.8V至5.5V,非常适合便携式应用,音频信号路由,视频切换,移动和通信系统。 它在5V 25°C时提供4Ω导通电阻Max,并具有极...

    android 飞机游戏

    在Android平台上开发游戏,尤其是飞行射击类(STG,Shoot 'em up)游戏,涉及到许多技术点和步骤。本文将详细解析"android 飞机游戏"这一项目,包括其核心概念、关键技术以及可能遇到的问题。 一、Android游戏开发...

    J2ME环形子弹 在STG游戏中使用

    STG(Shoot 'em up),又称射击游戏,是一种快节奏的动作游戏类型,玩家通常需要控制飞行器或角色躲避敌人的攻击并反击。在STG游戏中,"环形子弹"是一种常见的设计元素,它增加了游戏的视觉效果和挑战性。 J2ME中的...

    高分项目,基于Unity3D开发实现的射击游戏Flip The Gun,内含完整源码+资源

    高分项目,基于Unity3D开发实现的射击游戏Flip The Gun,内含完整源码+资源 射击游戏(Shooting game),简称为STG。游戏类型的一种,也是动作游戏的一种。射击游戏带有很明显的动作游戏特点,也没有纯然的射击游戏,因为...

    [测试] 改进测试进程 (Stg 版本) (英文版)

    [Rocky Nook] 改进测试进程 (Stg 版本) (英文版) [Rocky Nook] Improving the Test Process Stg Edition (E-Book) ☆ 图书概要:☆ This book covers the syllabus for the Improving the Test Process module of...

    PlanetBuster3:Danmaku STG游戏

    《PlanetBuster3:弹幕STG游戏》是一款基于JavaScript技术开发的射击类游戏,具有高速、华丽的弹幕效果,为玩家带来刺激的空战体验。在这款游戏中,玩家将扮演一位勇猛的飞行员,驾驶飞船穿越密集的弹幕,击败强大的...

    TestSTG4:另一个快速制作东方风格STG游戏的引擎

    Test STG4是一个用于iOS和macOS的实验性地狱引擎,用于创建东方风格的游戏 该引擎是和 建造 这只是一个Xcode项目。 建立在Big Sur 11.2.1和Xcode 12.2之上 特征 标有NEW的功能是TestSTG系列的新功能 iOS和macOS支持...

    基于C++的MFC期末作业-STG小游戏+源代码+文档说明

    在开始游戏后,场景中央的boss将开始发射弹幕。 使用方向键操控玩家躲避子弹。 按下shift键将能够进入低速模式,能以更高精度移动,并将显示判定点。 游戏时间持续90秒,游戏将记录您的中弹次数 当90秒结束后,游戏...

    高分项目,基于Unity3D开发实现的Advance Sniper Starter,内含完整源码+资源+unitypackage

    高分项目,基于Unity3D开发实现的Advance Sniper Starter Kit 射击游戏源码,内含完整源码+资源+unitypackage 射击游戏(Shooting game),简称为STG。游戏类型的一种,也是动作游戏的一种。射击游戏带有很明显的动作...

    stg.rar_direct_direct draw_stg

    本教程通过一个名为"stg.rar"的压缩包,提供了一个关于如何使用VC++(Visual C++)进行Direct Draw编程的实例,特别是针对游戏编程的入门学习。 在Direct Draw编程中,有几个关键概念和步骤是必不可少的: 1. **...

Global site tag (gtag.js) - Google Analytics