Java 2D单人游戏
创立基于Title的地图
在2D游戏中,地图是整体构造,或者咱们叫做游戏地图(game map),通常是几个屏幕的宽度示意。有些游戏地图是屏幕的20倍;甚至是100位以上,次要特点是跨屏幕之后,让地图滚动显示,这种类型的游戏又叫做2D平台游戏(2D platform game)。所以平台游戏是指玩家从一个平台跑到另外一平台,在其中须要跑、跳等动作,除此之外,还要避开敌人,以及采血、加膂力等动作。本章咱们介绍怎么创立根本的地图、地图文件、碰撞侦测、加膂力、简略的敌人,以及生成背景的视差滚动成果等。
如果在游戏中如果巨幅图片,这种策略不是最好的解决方案,因为这会产生大量的内存耗费,可能会导致不装载图片。另外,巨幅图片不能阐明玩家哪个地图能够应用,哪些地图不能够应用。解决这个问题的个别策略是应用基于title的图片。tile-base地图是把地图分解决成表格,每个单元格蕴含一小块的图片,或者没有图片。如下图示:
基于tile的地力点有点像应用预制块来创立游戏,不是同的就是这些块的色彩不,并且能够无限度应用色彩。Tile地力的蕴含的援用属于表格的每个单元格(cell)所有,这样,咱们只须要一些小图片就能够实现整个tile的画面显示,并且咱们能够依据游戏的需要无限度创立背景画面,而不放心内存的束缚问题。大多数游戏都应用16或者32位的图片来示意单元格,咱们这里应用是64位的图片,如下图所示:
以上就是基于tile的地图,它有九个块成果能够很容易决定哪些是“solid”局部,哪些是”empty”的地图,这样,你能够晓得哪局部地图玩家能够跳,哪局部玩家能够穿墙。上面咱们首先实现这个Tile地图。
TileMap类
package com.funfree.arklis.engine.tilegame;import java.awt.Image;import java.util.LinkedList;import java.util.Iterator;import com.funfree.arklis.graphic.*;/** 性能:书写一个类用来示意Tile地图 备注:该类蕴含的数据用来实现地图、包含小怪。每个tile援用一个图片。当然这些图片会被 应用屡次。 */public class TileMap{ private Image[][] tiles; //示意地图 private LinkedList sprites; //示意游戏中的小怪 private Sprite player; //示意玩家 /** 初始化成员变量时指定地图的宽和高 */ public TileMap(int width, int height){ tiles = new Image[width][height]; sprites = new LinkedList(); } /** 返回地图的宽度 */ public int getWidth(){ return tiles.length; } /** 获取地图的高度 */ public int getHeight(){ return tiles[0].length; } /** 依据指定的坐标来获取相应的tile。如果返回null值,那么示意地图越界了。 */ public Image getTile(int x, int y){ if(x < 0 || x >= getWidth() || y < 0 || y >= getHeight()){ //那么回返null值 return null; }else{ //否则返回tile return tiles[x][y]; } } /** 依据指定的坐标和指定的图片来更换tile */ public void setTile(int x, int y, Image tile){ tiles[x][y] = tile; } /** 返回玩家角色 */ public Sprite getPlayer(){ return player; } /** 指定玩家角色 */ public void setPlayer(Sprite player){ this.player = player; } /** 增加小怪地图中 */ public void addSprite(Sprite sprite){ sprites.add(sprite); } /** 删除该地图中的小怪 */ public void removeSprite(Sprite sprite){ sprites.remove(sprite); } /** 迭代出该地图中所有的小怪,除了玩家自身之外。 */ public Iterator getSprites(){ return sprites.iterator(); }}
除了地图,TileMap还蕴含在游戏中的小怪,小怪能够地图中任何地位,并且没有边界的限度。如下图:
装载Title的地图
上面咱们须要有一个中央来保留该地图,而后在失当的时候理论创立该地图。Tile地图游戏总是有多个级别的地图,该示例也不例外。如果咱们能够很轻松的形式来创立多个地图,那么玩家能够在实现一个地图之后,而后开始下一个地图的游戏情节。咱们创立地图时呼叫TileMap的addTile()办法和addSprite()办法,该办法的灵活性十分好,然而,这样编辑地图的级别比拟艰难,并且代码自身也不是很优雅。所以,大多数的tile游戏有本人的地图编辑器来创立地图。这个地图编辑器是可视化增加tile和小怪到游戏中,这样做的形式是十分简捷的形式。个别把地图保留到中介地图文件中,而这个文件是能够让游戏解析的。这样,咱们可定义一个基于文本地图文件,这样咱们能够编辑地图,因为tile是被定义在一个表格中的,所以文本文件中的每个字符能够示意一个tile或者是一个小怪/玩家,如下图:
其中”#”示意正文,而其它的示意tile的row。该地图是固定的,所以能够咱们可让地图变量,并且可能增加更多的line或者让line更长。那么解析地图的步骤有三步:
- 读取每一行,疏忽正文行,而后把每行放到一个汇合中
- 创立一个TileMap对象,TileMap的宽度就是汇合中最长元素的长度值,而高度就是汇合中的line的数量
- 解析每一line中的每个字符,依据该字符增加相应的tile或者sprite到地图中去。
实现这个工作是ResourceManager类。须要留神的是:增加sprite到TileMap中去时,开始,咱们须要创立不同的Sprite对象,这样,咱们可依据这些“主”怪来克隆小怪;第二,每个sprite不须要尺寸与tile的尺寸一样,所以,咱们须要保障每个sprite在tile中的地方,这些事件都在addSprite()办法实现。本章以前的Sprite的地位雷同的屏幕,然而在本章示例中,sprite的地位是雷同到tile地图。咱们应用TileMapRender的静态方法titlesToPixels()来转换tile地位到地图的地位。该函数乘以tile的数值,
int pixelSize = numTiles * TITLE_SIZE;
以上公式能够让sprite挪动到地图上的任意一个地位,并且不须要调整tile的边界。也就是说,咱们有一个灵便的形式来创立地图和解析它们,以及创立一个TileMap对象。在示例中,所有的地图都在map文件夹中(map1.txt和map2.txt)等等。如果咱们须要下一个地图,只须要让代码去寻找下一个地图文件即可;如果没有找到,代码回装载第一个地图。也就是说,咱们不须要新地图,只须要在这个目录中删除地图文件即可,也不须要通知游戏有多少个地图存在。
ResourceManager类
package com.funfree.arklis.engine.tilegame;import java.awt.*;import java.awt.geom.AffineTransform;import java.io.*;import java.util.ArrayList;import javax.swing.ImageIcon;import com.funfree.arklis.graphic.*;import com.funfree.arklis.engine.tilegame.sprites.*;/** 性能:书写一个ResourceManager类用来装载和治理tile图片和“主”怪。游戏中的 小怪是从“主”怪克隆而来。 备注:该类有第四章GameCore所有性能。*/public class ResourceManager { private ArrayList tiles; //保留文字地图的汇合 private int currentMap; //以后地图 private GraphicsConfiguration gc; //显示卡 // 用来被克隆的主怪 private Sprite playerSprite; private Sprite musicSprite; private Sprite coinSprite; private Sprite goalSprite; private Sprite grubSprite; private Sprite flySprite; /** 应用指定的显卡来创立资源管理器对象 */ public ResourceManager(GraphicsConfiguration gc) { this.gc = gc; loadTileImages(); loadCreatureSprites(); loadPowerUpSprites(); } /** 从images目录获取图片 */ public Image loadImage(String name) { String filename = "images/" + name; return new ImageIcon(filename).getImage(); } //获取Mirror图片 public Image getMirrorImage(Image image) { return getScaledImage(image, -1, 1); } //获取反转后的图片 public Image getFlippedImage(Image image) { return getScaledImage(image, 1, -1); } /** 实现图片的转换性能 */ private Image getScaledImage(Image image, float x, float y) { // 设置一个图片转换对象 AffineTransform transform = new AffineTransform(); transform.scale(x, y); transform.translate( (x-1) * image.getWidth(null) / 2, (y-1) * image.getHeight(null) / 2); // 创立通明的图片(不同半透明) Image newImage = gc.createCompatibleImage( image.getWidth(null), image.getHeight(null), Transparency.BITMASK); // 绘制通明图片 Graphics2D g = (Graphics2D)newImage.getGraphics(); g.drawImage(image, transform, null); g.dispose(); return newImage; } /** 从maps目录中装载下一下地图 */ public TileMap loadNextMap() { TileMap map = null; while (map == null) { currentMap++; try { map = loadMap( "maps/map" + currentMap + ".txt"); } catch (IOException ex) { if (currentMap == 1) { // 无装载的地图,返回null值! return null; } currentMap = 0; map = null; } } return map; } /** 从新装载maps目录下的地图文本 */ public TileMap reloadMap() { try { return loadMap( "maps/map" + currentMap + ".txt"); } catch (IOException ex) { ex.printStackTrace(); return null; } } /** 实现装载地图的外围办法 */ private TileMap loadMap(String filename)throws IOException{ ArrayList lines = new ArrayList(); int width = 0; int height = 0; // 读取文本文件中的每一行内容到汇合中保留 BufferedReader reader = new BufferedReader( new FileReader(filename)); for(;;) { String line = reader.readLine(); // 没有内容可读取了 if (line == null) { reader.close(); break; } // 增加每一行记录,除了正文 if (!line.startsWith("#")) { lines.add(line); width = Math.max(width, line.length()); } } // 解析每一行,以便创立TileEngine对象 height = lines.size(); TileMap newMap = new TileMap(width, height); for (int y = 0; y < height; y++) { //获取汇合中的字符串对象 String line = (String)lines.get(y); //把每个字符中的字符取出来 for (int x = 0; x < line.length(); x++) { char ch = line.charAt(x); // 查看字符是否为A,B,C等字符 int tile = ch - 'A'; //如果是字符A if (tile >= 0 && tile < tiles.size()) { //那么依据tile值来创立地图元素--这里地图实现的外围办法 newMap.setTile(x, y, (Image)tiles.get(tile)); } // 如果字符是示意小怪的,比方0, !或者*,那么别离增加主怪到汇合中 else if (ch == 'o') { addSprite(newMap, coinSprite, x, y); }else if (ch == '!') { addSprite(newMap, musicSprite, x, y); }else if (ch == '*') { addSprite(newMap, goalSprite, x, y); }else if (ch == '1') { addSprite(newMap, grubSprite, x, y); }else if (ch == '2') { addSprite(newMap, flySprite, x, y); } } } // 增加玩家到地图中去 Sprite player = (Sprite)playerSprite.clone(); player.setX(TileMapRenderer.tilesToPixels(3)); player.setY(0); newMap.setPlayer(player); //返回新的tile地图对象 return newMap; } /** 增加小怪到地图中去,并且是指定是的地位。 */ private void addSprite(TileMap map,Sprite hostSprite, int tileX, int tileY){ if (hostSprite != null) { // 从“主”怪克隆小怪 Sprite sprite = (Sprite)hostSprite.clone(); // 把小怪置中 sprite.setX( TileMapRenderer.tilesToPixels(tileX) + (TileMapRenderer.tilesToPixels(1) - sprite.getWidth()) / 2); // 在底部调试该小怪 sprite.setY( TileMapRenderer.tilesToPixels(tileY + 1) - sprite.getHeight()); // 增加该小怪到地图中去 map.addSprite(sprite); } } // ----------------------------------------------------------- // 实现装载小怪和图片的代码 // ----------------------------------------------------------- public void loadTileImages() { //保留查找A,B,C等字符,这样能够十分不便的删除images目录下的tiles tiles = new ArrayList(); char ch = 'A'; while (true) { String name = "tile_" + ch + ".png"; File file = new File("images/" + name); if (!file.exists()) { break; } tiles.add(loadImage(name)); ch++; } } public void loadCreatureSprites() { //申明一个图片至多保留4个图片的数组 Image[][] images = new Image[4][]; // 装载右边朝向的图片 images[0] = new Image[] { //装载玩家图片 loadImage("player1.png"), loadImage("player2.png"), loadImage("player3.png"), //装载苍蝇图片 loadImage("fly1.png"), loadImage("fly2.png"), loadImage("fly3.png"), //装载蠕虫图片 loadImage("grub1.png"), loadImage("grub2.png"), }; images[1] = new Image[images[0].length]; images[2] = new Image[images[0].length]; images[3] = new Image[images[0].length]; for (int i = 0; i < images[0].length; i++) { // 装载右朝向的图片 images[1][i] = getMirrorImage(images[0][i]); // 装载左朝向“死亡”图片 images[2][i] = getFlippedImage(images[0][i]); // 装载右朝向“死亡”图片 images[3][i] = getFlippedImage(images[1][i]); } // 创立creature动画对象 Animation[] playerAnim = new Animation[4]; Animation[] flyAnim = new Animation[4]; Animation[] grubAnim = new Animation[4]; for (int i = 0; i < 4; i++) { playerAnim[i] = createPlayerAnim( images[i][0], images[i][1], images[i][2]); flyAnim[i] = createFlyAnim( images[i][3], images[i][4], images[i][5]); grubAnim[i] = createGrubAnim( images[i][6], images[i][7]); } // 创立creature小怪(包含玩家) playerSprite = new Player(playerAnim[0], playerAnim[1], playerAnim[2], playerAnim[3]); flySprite = new Fly(flyAnim[0], flyAnim[1], flyAnim[2], flyAnim[3]); grubSprite = new Grub(grubAnim[0], grubAnim[1], grubAnim[2], grubAnim[3]); } //依据指定的图片来创立玩家动画对象 private Animation createPlayerAnim(Image player1,Image player2, Image player3){ Animation anim = new Animation(); anim.addFrame(player1, 250); anim.addFrame(player2, 150); anim.addFrame(player1, 150); anim.addFrame(player2, 150); anim.addFrame(player3, 200); anim.addFrame(player2, 150); return anim; } //依据指定的图片来创立苍蝇动画对象 private Animation createFlyAnim(Image img1, Image img2,Image img3){ Animation anim = new Animation(); anim.addFrame(img1, 50); anim.addFrame(img2, 50); anim.addFrame(img3, 50); anim.addFrame(img2, 50); return anim; } //依据指定的图片来创立蠕虫动画对象 private Animation createGrubAnim(Image img1, Image img2) { Animation anim = new Animation(); anim.addFrame(img1, 250); anim.addFrame(img2, 250); return anim; } private void loadPowerUpSprites() { // 创立“心”怪用来加膂力 Animation anim = new Animation(); anim.addFrame(loadImage("heart1.png"), 150); anim.addFrame(loadImage("heart2.png"), 150); anim.addFrame(loadImage("heart3.png"), 150); anim.addFrame(loadImage("heart2.png"), 150); goalSprite = new PowerUp.Goal(anim); // 创立 "星"怪,用来加分 anim = new Animation(); anim.addFrame(loadImage("star1.png"), 100); anim.addFrame(loadImage("star2.png"), 100); anim.addFrame(loadImage("star3.png"), 100); anim.addFrame(loadImage("star4.png"), 100); coinSprite = new PowerUp.Star(anim); // 创立“音乐”怪用来减速玩家 anim = new Animation(); anim.addFrame(loadImage("music1.png"), 150); anim.addFrame(loadImage("music2.png"), 150); anim.addFrame(loadImage("music3.png"), 150); anim.addFrame(loadImage("music2.png"), 150); musicSprite = new PowerUp.Music(anim); }}
图片起源:http://www.cungun.com/ 游戏