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

魍魉校园(Java版AVG游戏开发入门)源码分析

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


下载地址:http://code.google.com/p/loon-simple/downloads/list

声明一下,这个程序不是我写的,是cping1982写的。本人在这里斗胆分析一下高手5年前写的代码,一来是提高自己,二来也是给众多小白以信心和勇气,分析完源码你会发现用java写一个AVG游戏还是不难的。
如果你无法访问google code,也可在本文文末下载我已经加上了注释的版本。

原作者的博客上有3篇文章,可谓说相当好。说的东西都是点到为止,需要读者自己去结合源码细细品味。
Java版AVG游戏开发入门[0]——游戏模式转换中的事件交互
Java版AVG游戏开发入门[1] —— CG的绘制
Java版AVG游戏开发入门示例[3]——脚本引擎的制作及应用

我这里稍微再详细的分析一点源码。

1. 游戏模式转换

IControl的抽象化是关键,可以避免像STG雷霆行动那样冗长的if else分支判断现在处于哪种模式,使得扩展更简单。其实我们看一下这张序列图可以发现,一切的东西都是绘制到画布上的(AVGCanvas.paint方法),只是“如何画”这个逻辑给分散到了不同的IControl子类里。而AVG.setControl可以达到方便的切换模式的作用。


IControl有这些子类
Title 标题模式
Script 游戏中模式(采用了脚本系统)
AqueductGame 水渠贯通小游戏模式



IControl有如下这些方法可供子类实现
invoke 转换控制器接口
draw  画图
mouseMoved 鼠标处理
mousePressed 鼠标处理
keyPressed 键盘处理

这样添加新的模式就很简单了,各个模式之间相对做到解耦,而且功能也都有了。

2. CG
就是实现IControl各个子类的draw方法。如何画?主要步骤就是先画背景图,再画前景。

下面举1例
2.1 Title的绘制


public void draw(Graphics g) {
	//1.先绘制背景图
	graphics.drawImage(title, 0, 0, null);
	//2.画窗体蓝色背景
	Message.setWindowMessage(graphics, MESSAGE_LINE_X1, MESSAGE_LINE_Y1,
			MESSAGE_LINE_X2, MESSAGE_LINE_Y2);
	//3.画窗体边框
	Message.setWindowFrame(graphics, MESSAGE_LINE_X1, MESSAGE_LINE_Y1,
			MESSAGE_LINE_X2, MESSAGE_LINE_Y2);
	//4.画菜单选项
	for (int i = 0; i < messages.length; i++) {
		// 选中
		if (i == selectFlag) {
			// 变更字体颜色。
			graphics.setColor(Color.white);
			// 设定浮标。
			Message.setWindowBuoyageMessage(selectFlag, 160, 34, graphics,
							MESSAGE_LINE_X1, MESSAGE_LINE_Y1,
							MESSAGE_LINE_X2 + fontSize + 2, MESSAGE_LINE_Y2
									+ fontSize + 40);
		// 未选中
		} else {
			graphics.setColor(Color.gray);
		}
		//一个字一个字的画出来
		for (int k = 0; k < messages[i].length(); k++) {
			Utility.drawDefaultString(messages[i].substring(k, k + 1).toString(), graphics, font * k + left, i
							* (font + fontSize) + font + top, 1, font);
			
		}

	}

	g.drawImage(screen, 0, 0, null);
	g.dispose();
}


用的是RPG Maker XP格式的窗体图。



第一步简单,把背景图画出来即可。
第二步如图,A区绘制窗体蓝色背景,会有一个比例扩大。
第三步如图,需要将B区的通过比例换算分8步画出来(上,下,左,右,还有4个角)
第四步画菜单选项。需要根据鼠标的移动高亮选中的菜单项。

同时,mouseMoved方法处理鼠标的移动,赋值selectFlag,以待下一次绘图时高亮菜单选项。
public void mouseMoved(MouseEvent e) {
	super.mouseMoved(e);
	int k = (Control.mouse.y - top) / (font + fontSize);
	//如果鼠标太上面
	if (k < 0) {
		return;
	}
	//如果鼠标太下面
	if (k >= messages.length) {
		return;
	}
	//赋值selectFlag,以待下一次绘图时高亮菜单选项
	for (int l = 0; l < messages.length; l++) {
		if (l == k) {
			selectFlag = k;
			continue;
		}
	}
	//如果鼠标x轴在框内,则标记“选中”
	if ((double) Control.mouse.x > this.MESSAGE_LINE_X1
			&& (double) Control.mouse.x < this.MESSAGE_LINE_X1
					+ (double) this.MESSAGE_LINE_X2
			&& (double) Control.mouse.y > this.MESSAGE_LINE_Y1
			&& (double) Control.mouse.y < this.MESSAGE_LINE_Y1
					+ (double) this.MESSAGE_LINE_Y2) {
		select = true;
	} else {
		select = false;
	}
}


3. 脚本系统

脚本系统的运用一方面可以使得开发效率提高,bug更少,另一方面可以使得后期维护简单。脚本系统一旦成熟,不懂java的人也可以维护甚至开发一个新的游戏。

比如下面脚本显示背景,再显示了2个人物(xiaoyanyan和ranmin)。
cg del
gb image/ghost.png
cg chara/xiaoyanyan.png 0
cg chara/ranmin.png 200
cgwait


原理和之前Title画面类似,主要就是nextScript方法执行脚本,赋给变量(修改model层),而draw方法根据变量不同来画图(view层)。两个方法搭配,比较好的实现了MVC模式。
修改model层
private synchronized int nextScript(final int index, final String s) {
        //......
	for (j = index; j < scriptContent.length; j++) {
		//显示信息
		if (messageFlag.equalsIgnoreCase("mes")) {
			isMessage = true;
			break;
		}
		//背景
		if (messageFlag.equalsIgnoreCase("gb")) {
			if (objectFlag == null) {
				return index;
			}
			if (objectFlag.equalsIgnoreCase("none")) {
				cg.setBackgroundCG(null);
			} else {
				cg.setBackgroundCG(Utility.loadImage(objectFlag));
			}
			continue;
		}
		//人物
		if (messageFlag.equalsIgnoreCase("cg")) {
			if (objectFlag == null) {
				return index;
			}
			if (objectFlag.equalsIgnoreCase("del")) {
				//删除原有人物
				if (orderFlag != null) {
					cg.removeImage(orderFlag);
				} else {
					cg.clear();
				}
			} else {
				//添加人物
				int x = 0, y = 0;
				if (orderFlag != null) {
					x = Integer.parseInt(orderFlag);
				}
				if (gotoFlag != null) {
					y = Integer.parseInt(gotoFlag);
				}
				cg.addImage(objectFlag, x, y);
			}
			continue;
		}
	}
}


view层
public void draw(final Graphics g) {
	//1.画背景,带晃动
	if (cg.getBackgroundCG() != null) {
		if (shakeNumber > 0) {
			//通过随机数,达到图片在小范围内晃动效果
			graphics.drawImage(cg.getBackgroundCG(),
					shakeNumber / 2 - Control.rand.nextInt(shakeNumber),
					shakeNumber / 2 - Control.rand.nextInt(shakeNumber), null);
		} else {
			graphics.drawImage(cg.getBackgroundCG(), 0, 0, null);
		}
	}
	//2.画人物
	for (int i = 0; i < cg.getCharas().size(); i++) {
		Chara chara = (Chara) cg.getCharas().get(i);
		graphics.drawImage(chara.getCharacterCG(), chara.getX(), chara
				.getY(), null);
	}
}


附件有比较详细的注释,可以参考。
  • 大小: 6.9 KB
  • 大小: 6.8 KB
  • 大小: 2.9 KB
  • 大小: 354.4 KB
  • 大小: 243.5 KB
  • 大小: 4.3 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics