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

0.前言
本例子取自cping1982早期公开的一个AVG源码,loon-simple-20090212,里面带了6个游戏。这次我们要分析的是AVGSimple这个游戏。截图如下:
[img]http://dl2.iteye.com/upload/attachment/0104/7489/a587c7de-da0d-3840-a961-9cc69a143aeb.png[/img]

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

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

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

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

1. 游戏模式转换

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

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

[img]http://dl2.iteye.com/upload/attachment/0104/7491/d3b9227d-d099-3853-8c46-3c0c909457c1.png[/img]

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

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

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

下面举1例
2.1 Title的绘制
[img]http://dl2.iteye.com/upload/attachment/0104/7486/1a4d1300-9dbf-3b67-9896-a97b2f671af6.png[/img]


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格式的窗体图。
[img]http://dl2.iteye.com/upload/attachment/0104/7482/6c98a610-9f1c-3b7d-bd38-062ece5789f5.png[/img]
[img]http://dl2.iteye.com/upload/attachment/0104/7484/b6ca7ae8-88fb-380c-9b3a-2acfe1cd677b.png[/img]

第一步简单,把背景图画出来即可。
第二步如图,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);
}
}


附件有比较详细的注释,可以参考。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值