- 浏览: 724971 次
- 性别:
- 来自: 上海
文章分类
- 全部博客 (168)
- CSS (1)
- Eclipse (4)
- English (1)
- ExtJS (1)
- Git (3)
- Grails (3)
- Groovy (2)
- Hadoop (7)
- HTML5 (2)
- JavaScript (3)
- Maven (2)
- MQ (5)
- MyBatis (3)
- NodeJS (6)
- NoSQL (4)
- Oracle (6)
- PDF (3)
- Python (9)
- Redis (17)
- Tomcat (2)
- Unix (8)
- Web Service (6)
- 安全 (1)
- 电子书 (6)
- 工具 (1)
- 其他 (21)
- 人工智能 (2)
- 视觉 (2)
- 算法 (6)
- 图表 (1)
- 网络 (13)
- 性能 (5)
- 游戏 (9)
- 字节码 (3)
- 机器学习 (1)
最新评论
-
lijunwyf:
cevin15 写道可以看下这个开源软件,https://gi ...
用markdown2html把md转换成html -
cevin15:
可以看下这个开源软件,https://github.com/c ...
用markdown2html把md转换成html -
Raina:
运行不了呢……提示错误无法加载主类Baiduwallpaper ...
用Java更换Windows桌面壁纸 -
苏城细雨沐秋风:
我把解码的jar添加到类路径后,mp3可以播放,但是flac和 ...
java播放mp3/ogg/ape/flac音乐 -
peishuai1987:
请问楼主现在怎么样了,读了很多源码吗,比如mybatis、sp ...
mybatis源码阅读心得
1.框架与环境搭建
1.1 本游戏使用了以下框架
slick2d
lwjgl
首先去这两个网站分别将他们下载下来,(注意目前slick2d暂不支持lwjgl 3,所以需要下载lwjgl 2)然后建一个eclipse工程。
游戏源码(java,图片声音地图数据)可在我之前一篇博文中下载,本文文末代码只含有我加了中文注释的java代码。
1.2 lib目录下加入如下jar包
ibxm.jar
jinput.jar
jnlp.jar
jogg-0.0.7.jar
jorbis-0.0.15.jar
lwjgl.jar
slick.jar
1.3 native目录下加入如下本地库(本文以windows7 32位为例,其他类似)
jinput-dx8.dll
jinput-raw.dll
lwjgl.dll
OpenAL32.dll
1.4 关联native库
如图,展开lwjgl.jar,填上native库位置。
1.5 解决编译错误
删除ApplicationGameContainer.java,ScalableGameContainer.java
(注:slick2d最后一次更新是2013年,貌似N久不维护了,有些API已经过期了,导致编译不过)
Main.java修改main函数,将ApplicationGameContainer替换为AppGameContainer
至此编译通过,环境搭建完毕。
2. 运行
要分析源码最好的方法就是先——玩游戏,熟悉一下功能。
游戏的功能:
原汁原味复刻FC版赤色要塞,但是画面分辨率提高了
2D卷轴
2种难度供选择
可以continue
玩家8方向移动
秘籍模式(输入上上下下左右左右ZX变30条命,且武器升级)
OGG音乐播放
加载百分比显示
多张图合成到一张图提高加载效率
瓷砖,地图,地形,方向,触发地图(敌人)
3.主类分析
3.1 Main
由于用的slick2d框架,所以继承BasicGame,然后实现3个方法(init,update,render)即可。可看到,一般2d游戏框架都是这样的,程序员只需实现3个方法,init负责资源加载,update负责更新模型,render负责绘图。剩下的通用功能框架会负责考虑,如双缓冲绘图,FPS,跳帧。
其中update和render又转而交给IMode来处理
3.2 IMode
老规矩,IMode的抽象化是关键,可以避免冗长的if else分支判断现在处于哪种模式,使得扩展更简单,达到方便切换模式的作用。
Main的init函数最后进入了LoadingMode
4. 资源加载
4.1 LoadingMode
该模式的使用可以不必等所有资源加载完毕,一开始就直接显示窗口,并提示用户加载进度。
如图
主要就是调用main.loadNext()
4.2 XMLPackedSheet使用
看一下如何加载精灵。用了slick2d的一个加载大图片的功能XMLPackedSheet
看一下png和xml是什么样地
xml里面存储了某个小图片的位置
这样的好处就是多张图合成到一张图可以提高加载效率。
其实,制作这种图有工具可以使用,如slick2d自带的packulike(下载来的slick.zip的tool目录下),或者imagepacker
4.3 大图绘制
加载如下
如结尾日落图片,数据在sunset.dat里,tile则在large-0.png,large-1.png里
现在将Main里面的loadNext函数最后改为setMode(new SunsetMode2(), gc);
看下如何显示结尾日落图片
最后拼合效果如下图
4.4 地图加载
瓷砖,即tile,用过地图编辑器的都很熟悉了,地图由许多tile拼接而成。
地图,记录了每一格究竟用哪个tile
地形,每一格是固体,还是水。。。?
方向我没研究。
触发地图(敌人)就是敌人登场表,记录敌人在哪一个。
4.5 一个查看地图的小程序
现在将Main里面的loadNext函数最后改为setMode(new StageViewMode(), gc);
然后运行StageViewMode,注意程序会将地图以截屏的方式保存到d盘根目录。d:/jackal{$x}{$y}.png, x和y是递增的数字。注意执行前备份d盘根目录的同名png文件,以防文件被覆盖造成惨剧。
下图是地图的一隅
游戏地图绘制大致就是以下流程:
先绘制底图,只是图片,没有碰撞检测
然后记录障碍物(固体,水。。。),控制玩家是否可以通过。
再加上敌人
下图是第一关整个地图,用的 Picture Merge Genius工具合成的。
本来想做个地图编辑器的,但是比较懒,没时间做了。
5. GameMode主程序
5.1 触发机关(敌人登场)
processTriggers函数判断镜头Y坐标是否达到敌人位置,若达到,则敌人出现(位置已由之前的loadTriggerMap载入)
敌人就不分析了,跟之前本博客分析的STG飞机游戏差不多。敌人都继承自一个共同的Enemy。Java语言就是这样,通过一层层抽象,实现了复杂的功能。
5.2 groupmap
游戏中有门,被炸开后会出现通路。还有房子被炸开后出现破房子,这些必须要改Stage.tileMap才行,是通过Stage.groupMap这个变量实现的。
6.结尾
java很好很强大,降低了游戏制作的门槛,得以圆我们以前做游戏的梦。不过通过分析一个游戏我们也发现了,游戏要做好,程序、图片、声音、关卡设计一个都不能少。程序决定了游戏的可玩性高不高,所以还是很重要的。
1.1 本游戏使用了以下框架
slick2d
lwjgl
首先去这两个网站分别将他们下载下来,(注意目前slick2d暂不支持lwjgl 3,所以需要下载lwjgl 2)然后建一个eclipse工程。
游戏源码(java,图片声音地图数据)可在我之前一篇博文中下载,本文文末代码只含有我加了中文注释的java代码。
1.2 lib目录下加入如下jar包
ibxm.jar
jinput.jar
jnlp.jar
jogg-0.0.7.jar
jorbis-0.0.15.jar
lwjgl.jar
slick.jar
1.3 native目录下加入如下本地库(本文以windows7 32位为例,其他类似)
jinput-dx8.dll
jinput-raw.dll
lwjgl.dll
OpenAL32.dll
1.4 关联native库
如图,展开lwjgl.jar,填上native库位置。
1.5 解决编译错误
删除ApplicationGameContainer.java,ScalableGameContainer.java
(注:slick2d最后一次更新是2013年,貌似N久不维护了,有些API已经过期了,导致编译不过)
Main.java修改main函数,将ApplicationGameContainer替换为AppGameContainer
至此编译通过,环境搭建完毕。
2. 运行
要分析源码最好的方法就是先——玩游戏,熟悉一下功能。
游戏的功能:
原汁原味复刻FC版赤色要塞,但是画面分辨率提高了
2D卷轴
2种难度供选择
可以continue
玩家8方向移动
秘籍模式(输入上上下下左右左右ZX变30条命,且武器升级)
OGG音乐播放
加载百分比显示
多张图合成到一张图提高加载效率
瓷砖,地图,地形,方向,触发地图(敌人)
3.主类分析
3.1 Main
public class Main extends BasicGame { public static final int DISPLAY_WIDTH = 1024; public static final int DISPLAY_HEIGHT = 960; public static final int SMALL_WIDTH = 512; public static final int SMALL_HEIGHT = 480; public IMode mode; public void init(GameContainer gc) throws SlickException { //...... try { //载入进度条,字体,类 loadProgressBar(); loadFont(); loadClasses(); } catch(Throwable t) { Log.error("Loading error", t); } //接受按键 input = new HumanInput(buttonMapping, gc); //秘籍 konamiCode = new KonamiCode(this); startPlayer(); resetNextFrameTime(); //进入loading模式 requestMode(Modes.LOADING, gc); } public void update(GameContainer gc, int delta) throws SlickException { //...... int count = 0; while(nextFrameTime <= Sys.getTime()) { //全屏切换 fullScreenToggleCheck(gc); //接收按键 input.snap(); //模式模型更新 mode.update(gc); nextFrameTime += (int)((Sys.getTimerResolution() * 0.01f) + 0.5f); if (++count == 8) { resetNextFrameTime(); break; } } } public void render(GameContainer gc, Graphics g) throws SlickException { //模式渲染 mode.render(gc, g); //...... } public static void main(String[] args) throws SlickException { java.awt.Toolkit.getDefaultToolkit(); Main main = new Main(); AppGameContainer appGameContainer = new AppGameContainer( new ScalableGame(main, DISPLAY_WIDTH, DISPLAY_HEIGHT, true), SMALL_WIDTH, SMALL_HEIGHT, false); try { appGameContainer.setIcon("icons/32x32.png"); } catch(Throwable t) { Log.error("Icon error", t); } appGameContainer.start(); } }
由于用的slick2d框架,所以继承BasicGame,然后实现3个方法(init,update,render)即可。可看到,一般2d游戏框架都是这样的,程序员只需实现3个方法,init负责资源加载,update负责更新模型,render负责绘图。剩下的通用功能框架会负责考虑,如双缓冲绘图,FPS,跳帧。
其中update和render又转而交给IMode来处理
3.2 IMode
public interface IMode { public void init(Main main, GameContainer gc) throws SlickException; public void update(GameContainer gc) throws SlickException; public void render(GameContainer gc, Graphics g) throws SlickException; }
老规矩,IMode的抽象化是关键,可以避免冗长的if else分支判断现在处于哪种模式,使得扩展更简单,达到方便切换模式的作用。
Main的init函数最后进入了LoadingMode
4. 资源加载
4.1 LoadingMode
该模式的使用可以不必等所有资源加载完毕,一开始就直接显示窗口,并提示用户加载进度。
如图
主要就是调用main.loadNext()
public float loadNext() throws Throwable { switch(loadIndex) { //加载声音 case 0: bossIntro = new Music("music/boss_intro.ogg", Song.STREAMING); break; //37 加载精灵 case 37: loadSprites(); break; //38 大图片 case 38: loadLargeImages(); break; //39 精灵大小,貌似是用作碰撞检测 case 39: loadSizes(); break; //40 关卡 case 40: loadStages(stages); break; case 41: //全部加载完进入介绍模式 requestMode(Modes.INTRO, gc); //requestMode(Modes.SUNSET, gc); //setMode(new SunsetMode2(), gc); //setMode(new StageViewMode(), gc); break; } return ++loadIndex / 42f; }
4.2 XMLPackedSheet使用
看一下如何加载精灵。用了slick2d的一个加载大图片的功能XMLPackedSheet
private void loadSprites() throws Throwable { XMLPackedSheet pack1 = new XMLPackedSheet( "images/sprites-1.png", "images/sprites-1.xml"); }
看一下png和xml是什么样地
xml里面存储了某个小图片的位置
<sheet> <sprite name="explosion-2.png" x="1" y="1" width="128" height="124" /> <sprite name="gray-jeep-1.png" x="131" y="1" width="96" height="96" /> </sheet>
这样的好处就是多张图合成到一张图可以提高加载效率。
其实,制作这种图有工具可以使用,如slick2d自带的packulike(下载来的slick.zip的tool目录下),或者imagepacker
4.3 大图绘制
加载如下
private void loadLargeImages() throws Throwable { sunset = loadExtraLargeImage("sunset", "large-0", "large-1"); map = loadLargeImage("map", "large-1"); jeepYeah = loadExtraLargeImage("jeep-yeah", "large-2", "large-3"); }
如结尾日落图片,数据在sunset.dat里,tile则在large-0.png,large-1.png里
现在将Main里面的loadNext函数最后改为setMode(new SunsetMode2(), gc);
看下如何显示结尾日落图片
public class SunsetMode2 implements IMode { public Main main; @Override public void init(Main main, GameContainer gc) throws SlickException { this.main = main; } @Override public void update(GameContainer gc) throws SlickException { } @Override public void render(GameContainer gc, Graphics g) throws SlickException { main.sunset.draw(0, 0); //main.map.draw(0, 0); //main.jeepYeah.draw(0, 0); } }
最后拼合效果如下图
4.4 地图加载
private void loadStage(int index, Stage stage) throws Throwable { //加载瓷砖,地图,地形,方向,触发地图(敌人) loadTiles(index, stage); loadMaps(index, stage); loadTypes(index, stage); loadDirections(index, stage); loadTriggerMap(stage.mapHeight, triggerSizes, index, stage); }
瓷砖,即tile,用过地图编辑器的都很熟悉了,地图由许多tile拼接而成。
地图,记录了每一格究竟用哪个tile
地形,每一格是固体,还是水。。。?
方向我没研究。
触发地图(敌人)就是敌人登场表,记录敌人在哪一个。
4.5 一个查看地图的小程序
现在将Main里面的loadNext函数最后改为setMode(new StageViewMode(), gc);
然后运行StageViewMode,注意程序会将地图以截屏的方式保存到d盘根目录。d:/jackal{$x}{$y}.png, x和y是递增的数字。注意执行前备份d盘根目录的同名png文件,以防文件被覆盖造成惨剧。
public class StageViewMode implements IMode { Image copy; GameMode gameMode; int xTile; int yTile; public Main main; public GameContainer gc; public IInput input; @Override public void init(Main main, GameContainer gc) throws SlickException { this.main = main; this.gc = gc; this.input = main.input; gameMode = new GameMode(); gameMode.setStage(0, main.stages[0], false); System.out.println("tilemap width=" +gameMode.tileMap.length+",height=" +gameMode.tileMap[0].length); } @Override public void update(GameContainer gc) throws SlickException { } @Override public void render(GameContainer gc, Graphics g) throws SlickException { for (int y = 29; y >= 0; y--) { for (int x = 31; x >= 0; x--) { main.draw(gameMode.tiles[gameMode.tileMap[y + yTile*30][x + xTile*32]], (x << 5) - 0, (y << 5) - 0); int type = gameMode.typesMap[y + yTile*30][x + xTile*32]; Color originalColor = g.getColor(); g.setColor(Color.red); g.drawRect(x << 5, y << 5, 32, 32); if (type==GameMode.TYPE_SOLID) { Color transparentColor = new Color(255, 0, 0, 0.5f); g.setColor(transparentColor); g.fillRect(x << 5, y << 5, 32, 32); } else if (type==GameMode.TYPE_SHIELD) { Color transparentColor = new Color(0, 255, 0, 0.5f); g.setColor(transparentColor); g.fillRect(x << 5, y << 5, 32, 32); } else if (type==GameMode.TYPE_WATER) { Color transparentColor = new Color(0, 255, 255, 0.5f); g.setColor(transparentColor); g.fillRect(x << 5, y << 5, 32, 32); } g.setColor(originalColor); //System.out.println("triggerMap.length->"+gameMode.triggerMap.length); int[][] triggers = gameMode.triggerMap[y + yTile*30]; for(int i = triggers.length - 1; i >= 0; i--) { int[] trigger = triggers[i]; //System.out.println("trigger->"+trigger[0]); int displatX = trigger[1]; boolean displayTrigger =false; if (trigger[1] >= Main.DISPLAY_WIDTH && xTile == 1) { displatX = trigger[1]- Main.DISPLAY_WIDTH; displayTrigger= true; } else if (trigger[1] < Main.DISPLAY_WIDTH && xTile == 0) { displatX = trigger[1]; displayTrigger= true; } if (displayTrigger) { if (trigger[0]==Triggers.SOLDIER_WALKER || trigger[0]==Triggers.SOLDIER_STATIONARY) { main.draw((main.enemySoldiers)[1][1], displatX, y <<5); } else if (trigger[0]==Triggers.GREEN_BOAT) { main.draw((main.greenBoats)[0], displatX, y <<5); } } } } } copy = new Image(Main.SMALL_WIDTH, Main.SMALL_HEIGHT); g.copyArea(copy, 0, 0); ImageOut.write(copy, "d:/jackal" + yTile + xTile + ".png"); if (xTile == 1) { xTile = 0; yTile++; } else { xTile++; } if (yTile > 11) { System.exit(0); } } }
下图是地图的一隅
游戏地图绘制大致就是以下流程:
先绘制底图,只是图片,没有碰撞检测
然后记录障碍物(固体,水。。。),控制玩家是否可以通过。
再加上敌人
下图是第一关整个地图,用的 Picture Merge Genius工具合成的。
本来想做个地图编辑器的,但是比较懒,没时间做了。
5. GameMode主程序
5.1 触发机关(敌人登场)
processTriggers函数判断镜头Y坐标是否达到敌人位置,若达到,则敌人出现(位置已由之前的loadTriggerMap载入)
敌人就不分析了,跟之前本博客分析的STG飞机游戏差不多。敌人都继承自一个共同的Enemy。Java语言就是这样,通过一层层抽象,实现了复杂的功能。
5.2 groupmap
游戏中有门,被炸开后会出现通路。还有房子被炸开后出现破房子,这些必须要改Stage.tileMap才行,是通过Stage.groupMap这个变量实现的。
6.结尾
java很好很强大,降低了游戏制作的门槛,得以圆我们以前做游戏的梦。不过通过分析一个游戏我们也发现了,游戏要做好,程序、图片、声音、关卡设计一个都不能少。程序决定了游戏的可玩性高不高,所以还是很重要的。
- jackal_chinese_comment.7z (64.7 KB)
- 下载次数: 10
发表评论
-
Java版赤色要塞
2015-01-15 13:44 1708老外居然用Java复刻了一个赤色要塞! 出处:http://m ... -
Escape the men's room(Java版密室逃脱)
2015-01-13 23:48 19371.概述 老外用Java做的2D密室逃脱游戏。个人认为挺不错的 ... -
freetts程序示例:报时程序和伪人机对话
2015-01-11 21:52 41300.概述 语音合成,是将人类语音用人工的方式所产生。若 ... -
Java2D API视觉特效
2015-01-07 21:40 10731.一个不错的例子 Killer Game Programmi ... -
魍魉校园(Java版AVG游戏开发入门)源码分析
2014-12-29 21:19 13860.前言 本例子取自cping19 ... -
java播放mp3/ogg/ape/flac音乐
2014-12-29 16:19 15827好吧,又要说一句了,java真是无所不能。 用java sou ... -
雷霆行动(STG飞机游戏)源码分析
2014-12-27 15:26 26630.前言 本例子取自cping19 ... -
使用jfugue来演奏mid音乐
2014-12-26 17:21 1869java真是无所不能,居然 ...
相关推荐
【标题】"Java版赤色要塞" 指的是一项使用Java编程语言重新实现经典游戏"赤色要塞"的项目。这个项目可能是为了学习和实践Java编程、游戏开发或者重温经典游戏的乐趣。在Java中开发游戏可以利用Java的跨平台特性,...
《赤色要塞》是一款经典的8位FC(Family Computer,即任天堂红白机)游戏,深受玩家喜爱。这款游戏在1980年代末期推出,由日本的KONAMI公司开发,以其独特的游戏机制、丰富的关卡设计和紧张刺激的战斗体验闻名。 ...
FC赤色要塞NES文件
本人收藏的MRP游戏,不需要联网更新。。。用废卡或把短信模式调为空号即可免费玩。。(论坛上有此类设置的介绍,可百度)
赤色要塞:运行VirtuaNes,选择“文件-〉打开”,选中目录rom里面的“赤色要塞.nes文件”,确定。选择“编辑-〉电影-〉重放”,选中目录rom里面的“赤色要塞.vmv文件”,确定,然后就可以开始欣赏了。 绿色兵团:...
主要实现通过JAVA 传入授权参数到 js-sdk 调出微信一键配置界面,可参考我的博客https://blog.csdn.net/maizang52/article/details/79851960, 初始界面没有优化,介意者请勿下载。
用途:根据PDF模板生成PDF文件,将数据库查询的数据插入到模板指定未知,然后生成新的PDF文件 原理: 代码说明:exprotPDF_Main 为主文件。...支持各种java调用数据库数据,生成PDF文件。多个版本供参考。
JDBC是Java平台的标准API,由Java SE(标准版)提供,用于与各种类型的数据库进行通信。它定义了一组接口和类,使得开发者无需关心底层数据库的具体细节,就能在Java应用程序中实现数据存取功能。MySQL Connector/J...
红白机游戏合集包含了众多经典游戏,如《魂斗罗》系列、《93超级魂》、《沙罗曼蛇》系列、《超级马里奥兄弟》、《赤色要塞》、《双截龙》、《飞龙之拳3》、《坦克大战》、《松鼠大作战》、《淘金者...
在压缩包中,除了源代码文件外,还包含了一些经典的NES游戏ROM文件,如"沙漠漫蛇.NES"、"魂斗罗.NES"、"赤色要塞.nes"、"绿色兵团.NES"、"超级玛莉.nes"、"马戏团.nes"。这些文件实际上是NES游戏的二进制数据,...
源码分析可以帮助我们理解控件的具体实现细节,如属性的getter和setter方法是如何工作的,以及如何在控件内部处理输入验证。通过阅读源码,开发者可以学习到自定义控件设计的最佳实践,以及如何在C#中有效利用属性...
压缩包内的文件名称列表包括了一些FC/NES游戏的ROM文件,例如“绿色兵团.NES”、“沙漠漫蛇.NES”、“魂斗罗.NES”、“赤色要塞.nes”、“超级玛莉.nes”、“马戏团.nes”。这些都是FC/NES平台上的知名游戏,这些ROM...
学院团委辅导赤色社团工作汇报沟通资料.docx
包含了四本书:《大型分布式网站架构设计与实践》、《大型网站系统与JAVA中间件实践》、《分布式Java应用基础与实践》、《核心原理与案例分析》。可为有java开发基础的开发人员提供从基础到深入的学习分布式技术
将多个word路径下的word合并成一个word 第一个路径合并后在第一个位置,最后一个路径合并后在最后
2. 赤色生命线(MA(CLOSE,56)):慢速移动平均线,代表长期趋势。 交易策略: - 当事情线从下方向上穿越生命线,这通常被视为买入信号,因为这表明短期趋势可能正在转强。 - 同时,当DIFF(MACD的快线)上穿DEA...
这个中文版文档使得中国开发者能更方便地理解和应用Java 1.8的新特性,提升开发效率。 在Java 1.8中,最显著的改变之一是Lambda表达式。这是一种简洁的匿名函数表示方式,可以用于简化那些只需要一次性的、短小的...
根据word模板导出word、PDF文档,功能全,内容多,介绍详细。主要实现根据word模板及模板中的坐标($[标明.字段名])的形式进行查询出所对应的数据,然后分别生成PDF 和 WORD功能。 本功能是将各个模板的数据均查询...