技术开发 频道

Java中2.5D游戏八方走法实现原理及相关代码

  【IT168技术文档】2.5D游戏,虽然在外观上近似于3D游戏,却又不是严格意义上讲的3D游戏,故此2.5D游戏又常被称为[伪3D游戏]。

  在笔者的观念中,2.5D严格上说并不能算是一种技术,而只是一种实现方式或者说应用手段。大多数时候,游戏公司之所以会采取2.5D方式开发游戏,常是为解决3D及2D技术混用而采取的一种折中,而并不是说这种手段有多么先进。2.5D游戏的实现方式虽然很多,但主要无非有三类,即:2D角色+3D场景(比如RO1)、3D角色+2D场景(比如生化复刻版)、2D角色+2D场景(比如仙剑),另外有些纯3D游戏出于操作性考虑而固定视角,勉强擦了个2.5D的边,但严格上讲依旧是3D。

  除了2D角色+2D场景是利用斜45度的2D图片来“冒充”3D效果以外,其它组合方式大多有真正的3D演算参与其中,不过是出于开发效率或者游戏风格等外部因素考虑而混入2D,可以说是一种产生在2D向3D演变过程中的过渡物。

  使用2.5D方式开发游戏的好处在于,能够免去纯3D图像渲染所涉及的海量运算,从而减低不必要的系统资源损耗,并且在处理单纯的2D画面时,贴图也明显较系统渲染为快,最主要的是——开发周期明显较3D游戏更短。

  当然,缺点也很明显,最核心的一点就够——不伦不类,非驴非马。

  具体到2.5D游戏开发,根据选择的技术不同,也会产生不同的实现手段,笔者在[Java中2.5D游戏(斜45度角)的设计与实现]系列博文中内所要介绍的,是最简单,同时也最方便的2D角色+2D场景——伪45度角方式,不需要的可以无视此文了,具体到真3D与2D的混合,笔者将留到介绍JME时再去讲解。

  在正式开始本回之前,我们先来简要介绍下不同维度空间的特点:

  零维,简单讲就是没有维度,表现形式上就是一个[点],没有长度,更没有高度。实际上,很多人认为宇宙最初就是个零维空间。

  一维,多体现为一条[直线],也可以理解为一个只有长度,而没有高度的无限线性空间。

  二维,即我们常说的[平面],由X及Y两点交织而成,即只有长与宽,典型的二维空间存在方式是数据表格。

  三维,三维即[立体],若谁不能理解三维,请随意向显示器四周看看,举目所及全是三维空间。从技术角度解释,三维就是在二维的长、宽基础上再加个高度(厚度)形成的体积面,典型的三维存在方式就是我们这个世界。

  四维,四维空间是个非常模糊的概念,实际上它象征着[n维]。在物理学及数学概念中,一个n的序列可以被理解为一个n维空间中的位置,当n=4时,所有这样的位置的集合都叫做四维空间,但是当n-1或者n+1时,它又会自然过渡成其它空间。这种空间与我们熟悉并在其中居住的三维空间不同,因为它多一个维度。这个额外的维度既可以理解成时间,也可以直接理解为空间的第四维,即第四空间维度。当我们说到四维空间时,常会扯出天堂、地狱、阴阳界等超自然理论,实在玄之又玄,笔者也不知道它究竟怎样表示才好……

  由于人类生存在三维空间,我们周围的空间自然也都具有三个维度(上下(长)、左右(宽)、前后(厚度)),用中式思维解释就是世有八荒(又称八方,即“东、西、南、北、东南、东北、西南、西北”八个方向),人居六合(即“上、下、东、西、南、北”六个方位),我们也都很自然的会用“八荒六合”来进行方位判定,当然,最先决的方位判定条件是“我在其中”。

  如果一个人能在“八荒六合”之内与我一样行动,我当然会认为他与我一样练成了八荒六合唯我独尊功……咳,我是说认为他与我同样是一个立体的人,而不是一张纸片,嗯嗯(=_=|||)。

  同样的道理,在游戏中如果一个2D Sprite的行动模式与3D Sprite一致,那么用户便很容易“误认”此单元为3D,而非2D。原因就在于,人眼是极好蒙蔽的,比如好莱坞早期大片中就经常使用纸制建筑来冒充城镇或者某个名声古迹;即便游戏2.5D游戏中没有实际的3D坐标及多面计算,只要能给人眼以“距离感”或者说“立体感”,我们也会认为这个游戏是三维存在的,而没人会去关心它是否真正使用了3D渲染方式。换句话说,只要我们创建的游戏单元“看上去”能“行八荒”,“游六合”,也就是看上去它的行为是3D立体的,玩家就会认为角色正处于一个三维空间之内,而不是穿越到某个2D世界。

  那么,这个“看上去移动”的效果要如何达到呢?

  我们用下图作为示例:

  通过上图中我们可以发现,此图中角色的单元动作被分解为八种不同类型,即上、左、右、下、左上、右上、左下、右下,而此八种动作正好对应着“八荒”中的“北、西、东、南、西北、东北、西南、东南”。由于人眼所能观测到的运行是相对的,只要背景或者角色中任意一者发生移动,我们都会产生“移动”的“错觉”。所以我们并不追求角色的实际3D运动,而只是令角色单元能做出对应“上、左、右、下、左上、右上、左下、右下”这八方向的动作,在普通人眼中,便与角色移动向“北、西、东、南、西北、东北、西南、东南”这八个方向无异。

  因此,在2D制作2.5D效果时,每个角色的动作单元实际上都只是一幅分帧小图罢了。

  如何判定对应的单元图像呢?

  具体到单元动作的显示判定,和游戏采取的斜视角产生方式息息相关,大体上分可分两类,即非等距坐标实现与等距坐标实现。

  1、非等距坐标实现:

  逻辑如下图:  

  以0点为常模(参照物),在2D地图上“错位”绘制角色,每次角色移动时偏移相应坐标,配合图片产生45度移动错觉。

  优点:在编程上极容易实现,并且更利于地图及角色的联合操作。

  缺点:如果不对单元与地图的交织部分进行细节处理,很容易产生移动“生硬”感,并且很难融入一些较复杂的地形判定。

  2、等距坐标实现:

  逻辑如下图:

  实际上此图没有任何坐标变化,而是在不改变2D图形X,Y坐标系的基础上,等距转换坐标点位置为斜45度时的状态,由于坐标系经过等距转换,所以实际操作中与2D无疑。

  优点:除了坐标转换外,基本上还是2D那一套,纯2D时怎样处理便怎样处理。

  缺点:为了配合倾斜后的X,Y坐标拼接,大部分平面图必须转换为45度图,当然这是美工的事(^^)(PS:虽然也可以在平面图上自动换算出所需的斜视图形,但细节处通常不够理想,而且耗费不必要的运算资源,还是交给美工直接切出成品图最好,鄙人大原则就是能麻烦美工就不劳驾程序员(^^)),不过象笔者这样个人研究就超麻烦……

  在本系列博文在后面会涉及到此部分,在这里先给出一个基本概念。

  首先,要转换坐标为斜45度时位置,至少需要以下参数。

  1、mapX(地图X坐标)

  2、mapY(地图Y坐标)

  3、mapMaxY (Y轴的最大纵深)

  4、tileWidth(每块小图宽度)

  5、tileHeight(每块小图高度)

  而后我们才可以根据基础参数换算坐标位置:

  screenX (屏幕坐标X)

  screenY (屏幕坐标Y)

  screenX = (mapX - mapY + mapMaxY) * (tileWidth / 2);

  screenY = (mapX + mapY) * (tileHeight / 2);

  bevelMapX (倾视的X坐标)

  bevelMapY (倾视的Y坐标)

  bevelMapX = ((screenY / tileHeight) + (screenX - (mapMaxY * tileWidth/2)) / tileWidth);

  bevelMapY = ((screenY / tileHeight) - (screenX - (mapMaxY * tileWidth/2)) / tileWidth);

  这时得到的bevelMapX及bevelMapY,就是斜45度时的绘图位置,以此坐标绘制准备好的斜视图,就自然会呈现在斜视情况下的X,Y点位置上。

  由于本例中为非等距实现,也就是在2D地图上直接位移角色单元到斜点,故此不需要额外的进行地图坐标换算处理,但是人物坐标则需偏移。

  下面开始我们用代码示例说话:

  Role.java(负责描述一个角色单元的行为)

  1 package org.loon.game.simple.alldirection.rpg;
  2 import java.awt.Color;
  3 import java.awt.Graphics;
  4 import java.util.List;
  5 import org.loon.game.simple.alldirection.GraphicsUtils;
  6 public class Role implements Config {
  7     private static final int SPEED = 4;
  8     public static final double PROB_MOVE = 0.02;
  9     private int x, y;
10     private int px, py;
11     private int direction;
12     private int count;
13     private boolean isMoving;
14     private int movingLength;
15     private int moveType;
16     private String message;
17     private Thread threadAnime;
18     private RpgSprite sprite;
19     private RpgMap map;
20     private String name;
21     private String partyName;
22     private int ioffsetX;
23     private int ioffsetY;
24     private boolean autoFinder;
25     private boolean isLoop;
26     public Role(String fileName, int x, int y, int direction, int moveType,
27             RpgMap map) {
28         sprite = new RpgSprite(fileName);
29         this.x = x;
30         this.y = y;
31         px = x * CS;
32         py = y * CS;
33         ioffsetX = sprite.getImageWidth() - CS;
34         ioffsetY = sprite.getImageHeight() - CS;
35         this.direction = direction;
36         this.count = 0;
37         this.moveType = moveType;
38         this.map = map;
39         this.roleLoop();
40     }
41     private void roleLoop() {
42         isLoop = true;
43         threadAnime = new Thread(new AnimationThread());
44         threadAnime.start();
45     }
46     public void stop() {
47         isLoop = false;
48         threadAnime = null;
49     }
50     public void setXandY(Cell2D cell) {
51         setXandY(cell.x(), cell.y());
52     }
53     public void setXandY(int x, int y) {
54         this.x = x;
55         this.y = y;
56     }
57     public Cell2D getCell2D() {
58         return new Cell2D(x, y);
59     }
60     private synchronized void redress() {
61         if (autoFinder) {
62             move();
63         }
64         if (px < 0) {
65             px = 0;
66         }
67         if (py < 0) {
68             py = 0;
69         }
70         if (px > map.getWidth() - CS) {
71             px = map.getWidth() - CS;
72             x = map.getRow() - 1;
73         }
74         if (py > map.getHeight() - CS) {
75             py = map.getHeight() - CS;
76             y = map.getCol() - 1;
77         }
78     }
79     public synchronized void draw(Graphics g, int offsetX, int offsetY) {
80         redress();
81         int aspect = 0;
82         switch (direction) {
83         case UP:
84             aspect = RpgSprite.UPPER_RIGHT;
85             break;
86         case DOWN:
87             aspect = RpgSprite.LOWER_LEFT;
88             break;
89         case LEFT:
90             aspect = RpgSprite.UPPER_LEFT;
91             break;
92         case RIGHT:
93             aspect = RpgSprite.LOWER_RIGHT;
94             break;
95         case TUP:
96             aspect = RpgSprite.UP;
97             break;
98         case TDOWN:
99             aspect = RpgSprite.DOWN;
100             break;
101         case TLEFT:
102             aspect = RpgSprite.LEFT;
103             break;
104         case TRIGHT:
105             aspect = RpgSprite.RIGHT;
106             break;
107         }
108         int nx = px + offsetX - ioffsetX;
109         int ny = py + offsetY - ioffsetY;
110         g.drawImage(sprite.getMove(aspect)[count], nx, ny, null);
111         if (name != null) {
112             int fontHeight = g.getFontMetrics().getHeight();
113             int nameFontWidth = g.getFontMetrics().stringWidth(name);
114             int size = 20;
115             int mx = nx + ioffsetX - nameFontWidth / 2;
116             int my = ny + ioffsetY + size + fontHeight;
117             GraphicsUtils.drawStyleString(g, name, mx, my, Color.black,
118                     Color.white);
119             GraphicsUtils.drawStyleString(g, partyName, mx, my + size,
120                     Color.black, Color.white);
121         }
122     }
123     public synchronized void autoDirection(List startPath) {
124         Cell2D cell1 = (Cell2D) startPath.get(0);
125         try {
126             if (startPath.size() > 1) {
127                 Cell2D cell2 = (Cell2D) startPath.get(1);
128                 int sx = cell2.x() - cell1.x();
129                 int sy = cell2.y() - cell1.y();
130                 direction = Field2D.getDirection(sx, sy);
131             }
132         } finally {
133             startPath.remove(0);
134         }
135     }
136     public synchronized boolean move() {
137         switch (direction) {
138         case LEFT:
139             if (moveLowerLeft()) {
140                 return true;
141             }
142             break;
143         case RIGHT:
144             if (moveLowerRight()) {
145                 return true;
146             }
147             break;
148         case UP:
149             if (moveUpperRight()) {
150                 return true;
151             }
152             break;
153         case DOWN:
154             if (moveUpperLeft()) {
155                 return true;
156             }
157             break;
158         case TLEFT:
159             if (moveLeft()) {
160                 return true;
161             }
162             break;
163         case TRIGHT:
164             if (moveRight()) {
165                 return true;
166             }
167             break;
168         case TUP:
169             if (moveUp()) {
170                 return true;
171             }
172             break;
173         case TDOWN:
174             if (moveDown()) {
175                 return true;
176             }
177             break;
178         }
179         return false;
180     }
181     protected boolean moveLeft() {
182         int nextX = x - 1;
183         int nextY = y;
184         if (nextX < 0) {
185             nextX = 0;
186         }
187         if (!map.isHit(nextX, nextY)) {
188             px -= Role.SPEED;
189             movingLength += Role.SPEED;
190             if (movingLength >= CS) {
191                 x--;
192                 px = x * CS;
193                 isMoving = false;
194                 return true;
195             }
196         } else {
197             isMoving = false;
198             px = x * CS;
199             py = y * CS;
200         }
201         return false;
202     }
203     protected boolean moveRight() {
204         int nextX = x + 1;
205         int nextY = y;
206         if (nextX > map.getCol() - 1) {
207             nextX = map.getCol() - 1;
208         }
209         if (!map.isHit(nextX, nextY)) {
210             px += Role.SPEED;
211             movingLength += Role.SPEED;
212             if (movingLength >= CS) {
213                 x++;
214                 px = x * CS;
215                 isMoving = false;
216                 return true;
217             }
218         } else {
219             isMoving = false;
220             px = x * CS;
221             py = y * CS;
222         }
223         return false;
224     }
225     protected boolean moveUp() {
226         int nextX = x;
227         int nextY = y - 1;
228         if (nextY < 0) {
229             nextY = 0;
230         }
231         if (!map.isHit(nextX, nextY)) {
232             py -= Role.SPEED;
233             movingLength += Role.SPEED;
234             if (movingLength >= CS) {
235                 y--;
236                 py = y * CS;
237                 isMoving = false;
238                 return true;
239             }
240         } else {
241             isMoving = false;
242             px = x * CS;
243             py = y * CS;
244         }
245         return false;
246     }
247     protected boolean moveDown() {
248         int nextX = x;
249         int nextY = y + 1;
250         if (!map.isHit(nextX, nextY)) {
251             py += Role.SPEED;
252             movingLength += Role.SPEED;
253             if (movingLength >= CS) {
254                 y++;
255                 py = y * CS;
256                 isMoving = false;
257                 return true;
258             }
259         } else {
260             isMoving = false;
261             px = x * CS;
262             py = y * CS;
263         }
264         return false;
265     }
266     protected boolean moveLowerLeft() {
267         int nextX = x - 1;
268         int nextY = y - 1;
269         if (nextX < 0) {
270             nextX = 0;
271         }
272         if (nextY < 0) {
273             nextY = 0;
274         }
275         if (!map.isHit(nextX, nextY)) {
276             px -= Role.SPEED;
277             py -= Role.SPEED;
278             movingLength += Role.SPEED;
279             if (movingLength >= CS) {
280                 x--;
281                 px = x * CS;
282                 y--;
283                 py = y * CS;
284                 isMoving = false;
285                 return true;
286             }
287         } else {
288             isMoving = false;
289             px = x * CS;
290             py = y * CS;
291         }
292         return false;
293     }
294     protected boolean moveLowerRight() {
295         int nextX = x + 1;
296         int nextY = y + 1;
297         if (nextX > map.getRow() - 1) {
298             nextX = map.getRow() - 1;
299         }
300         if (nextY > map.getCol() - 1) {
301             nextY = map.getCol() - 1;
302         }
303         if (!map.isHit(nextX, nextY)) {
304             px += Role.SPEED;
305             py += Role.SPEED;
306             movingLength += Role.SPEED;
307             if (movingLength >= CS) {
308                 x++;
309                 px = x * CS;
310                 y++;
311                 py = y * CS;
312                 isMoving = false;
313                 return true;
314             }
315         } else {
316             isMoving = false;
317             px = x * CS;
318             py = y * CS;
319         }
320         return false;
321     }
322     protected boolean moveUpperLeft() {
323         int nextX = x - 1;
324         int nextY = y + 1;
325         if (nextX < 0) {
326             nextX = 0;
327         }
328         if (nextY > map.getCol() - 1) {
329             nextY = map.getCol() - 1;
330         }
331         if (!map.isHit(nextX, nextY)) {
332             px -= Role.SPEED;
333             py += Role.SPEED;
334             movingLength += Role.SPEED;
335             if (movingLength >= CS) {
336                 x--;
337                 px = x * CS;
338                 y++;
339                 py = y * CS;
340                 isMoving = false;
341                 return true;
342             }
343         } else {
344             isMoving = false;
345             px = x * CS;
346             py = y * CS;
347         }
348         return false;
349     }
350     protected boolean moveUpperRight() {
351         int nextX = x + 1;
352         int nextY = y - 1;
353         if (nextX > map.getRow() - 1) {
354             nextX = map.getRow() - 1;
355         }
356         if (nextY < 0) {
357             nextY = 0;
358         }
359         if (!map.isHit(nextX, nextY)) {
360             px += Role.SPEED;
361             py -= Role.SPEED;
362             movingLength += Role.SPEED;
363             if (movingLength >= CS) {
364                 x++;
365                 px = x * CS;
366                 y--;
367                 py = y * CS;
368                 isMoving = false;
369                 return true;
370             }
371         } else {
372             isMoving = false;
373             px = x * CS;
374             py = y * CS;
375         }
376         return false;
377     }
378     /**
379      * 触发事件
380      *
381      * @return
382      */
383     public Role talkWith() {
384         int nextX = 0;
385         int nextY = 0;
386         switch (direction) {
387         case LEFT:
388             nextX = x - 1;
389             nextY = y;
390             break;
391         case RIGHT:
392             nextX = x + 1;
393             nextY = y;
394             break;
395         case UP:
396             nextX = x;
397             nextY = y - 1;
398             break;
399         case DOWN:
400             nextX = x;
401             nextY = y + 1;
402             break;
403         }
404         Role chara;
405         chara = map.getRoles().roleCheck(nextX, nextY);
406         if (chara != null) {
407             switch (direction) {
408             case LEFT:
409                 chara.setDirection(RIGHT);
410                 break;
411             case RIGHT:
412                 chara.setDirection(LEFT);
413                 break;
414             case UP:
415                 chara.setDirection(DOWN);
416                 break;
417             case DOWN:
418                 chara.setDirection(UP);
419                 break;
420             }
421         }
422         return chara;
423     }
424     public int getX() {
425         return x;
426     }
427     public int getY() {
428         return y;
429     }
430     public int getPx() {
431         return px;
432     }
433     public int getPy() {
434         return py;
435     }
436     public void setDirection(int dir) {
437         direction = dir;
438     }
439     public synchronized boolean isMoving() {
440         return isMoving;
441     }
442     public synchronized void setMoving(boolean flag) {
443         isMoving = flag;
444         movingLength = 0;
445     }
446     public String getMessage() {
447         return message;
448     }
449     public void setMessage(String message) {
450         this.message = message;
451     }
452     public int getMoveType() {
453         return moveType;
454     }
455     private class AnimationThread extends Thread {
456         public void run() {
457             while (isLoop) {
458                 if (count < sprite.getSize()) {
459                     count++;
460                 } else {
461                     count = 0;
462                 }
463                 try {
464                     Thread.sleep(300);
465                 } catch (InterruptedException e) {
466                 }
467             }
468         }
469     }
470     public boolean isAutoFinder() {
471         return autoFinder;
472     }
473     public void setAutoFinder(boolean autoFinder) {
474         this.autoFinder = autoFinder;
475     }
476     public int getIoffsetX() {
477         return ioffsetX;
478     }
479     public int getIoffsetY() {
480         return ioffsetY;
481     }
482     public String getName() {
483         return name;
484     }
485     public void setName(String name) {
486         this.name = name;
487     }
488     public String getPartyName() {
489         return partyName;
490     }
491     public void setPartyName(String partyName) {
492         this.partyName = partyName;
493     }
494 }
495

  RprMap.java(负责描述角色单元集合在地图上的具体位置)

 

  1 package org.loon.game.simple.alldirection.rpg;
  2 import java.awt.Color;
  3 import java.awt.Graphics;
  4 import java.io.IOException;
  5 import java.util.List;
  6 import org.loon.game.simple.alldirection.GraphicsUtils;
  7 import org.loon.game.simple.alldirection.LSystem;
  8 public class RpgMap implements Config {
  9     private ImageMapFactory imageMap;
10     private Roles roles;
11     private boolean showGrid;
12     private Field2D map2d;
13     private int firstTileX;
14     private int firstTileY;
15     private int lastTileX;
16     private int lastTileY;
17     public RpgMap(String imageFile, String mapFile) {
18         try {
19             imageMap = new ImageMapFactory(imageFile, mapFile);
20         } catch (IOException e) {
21             throw new RuntimeException(e);
22         }
23         this.map2d = new Field2D(imageMap.getMap());
24         this.roles = new Roles();
25     }
26     public void addRole(Role role) {
27         roles.addChara(role);
28     }
29     public Role getHero() {
30         return roles.getHero();
31     }
32     public void setupHero(Role hero) {
33         roles.mainHero(hero);
34     }
35     public synchronized void draw(Graphics g, int offsetX, int offsetY) {
36         firstTileX = pixelsToTiles(-offsetX);
37         lastTileX = firstTileX + pixelsToTiles(LSystem.WIDTH) + 1;
38         lastTileX = Math.min(lastTileX, getRow());
39         firstTileY = pixelsToTiles(-offsetY);
40         lastTileY = firstTileY + pixelsToTiles(LSystem.HEIGHT) + 1;
41         lastTileY = Math.min(lastTileY, getCol());
42         for (int i = firstTileX; i < lastTileX; i++) {
43             for (int j = firstTileY; j < lastTileY; j++) {
44                 g.drawImage(imageMap.getImages()[i][j], tilesToPixels(i)
45                         + offsetX, tilesToPixels(j) + offsetY, null);
46                 if (showGrid) {
47                     if (imageMap.getMap()[j][i] == 1) {
48                         g.setColor(Color.white);
49                         g.drawRect(tilesToPixels(i) + offsetX, tilesToPixels(j)
50                                 + offsetY, CS - 2, CS - 2);
51                         GraphicsUtils.setAlpha(g, 0.5d);
52                         g.fillRect(tilesToPixels(i) + offsetX, tilesToPixels(j)
53                                 + offsetY, CS - 2, CS - 2);
54                         GraphicsUtils.setAlpha(g, 1.0d);
55                     } else if (imageMap.getMap()[j][i] == -1) {
56                         g.setColor(Color.blue);
57                         g.drawRect(tilesToPixels(i) + offsetX, tilesToPixels(j)
58                                 + offsetY, CS - 2, CS - 2);
59                         GraphicsUtils.setAlpha(g, 0.3d);
60                         g.fillRect(tilesToPixels(i) + offsetX, tilesToPixels(j)
61                                 + offsetY, CS - 2, CS - 2);
62                         GraphicsUtils.setAlpha(g, 1.0d);
63                     }
64                 }
65             }
66         }
67         roles.draw(g, offsetX, offsetY);
68     }
69     public int getSelfFirstX() {
70         return firstTileX;
71     }
72     public int getSelfFirstY() {
73         return firstTileY;
74     }
75     public int getSelfLastX() {
76         return lastTileX;
77     }
78     public int getSelfLastY() {
79         return lastTileY;
80     }
81     public int getSelfFirstWidth() {
82         return tilesToPixels(firstTileX);
83     }
84     public int getSelfFirstHeight() {
85         return tilesToPixels(firstTileY);
86     }
87     public int getSelfLastWidth() {
88         return tilesToPixels(lastTileX);
89     }
90     public int getSelfLastHeight() {
91         return tilesToPixels(lastTileY);
92     }
93     public List findPath(Role hero, Cell2D goal) {
94         return AStarFinder.find(map2d, hero.getCell2D(), goal);
95     }
96     public void showGrid(boolean show) {
97         this.showGrid = show;
98     }
99     public ImageMapFactory getFactory() {
100         return imageMap;
101     }
102     public boolean isHit(int x, int y) {
103         try {
104             int[][] map = imageMap.getMap();
105             if (map[y][x] == 1) {
106                 return true;
107             }
108             if (roles.isHit(x, y)) {
109                 return true;
110             }
111             return false;
112         } catch (Exception e) {
113             return false;
114         }
115     }
116     public static int pixelsToTiles(double pixels) {
117         return (int) Math.floor(pixels / CS);
118     }
119     public static int tilesToPixels(int tiles) {
120         return tiles * CS;
121     }
122     public int getRow() {
123         return imageMap.getMapWidth();
124     }
125     public int getCol() {
126         return imageMap.getMapHeight();
127     }
128     public int getWidth() {
129         return imageMap.getImageWidth();
130     }
131     public int getHeight() {
132         return imageMap.getImageHeight();
133     }
134     public Roles getRoles() {
135         return roles;
136     }
137 }

 

  Main.java(初始配置及程序运行)

1 package org.loon.game.simple.alldirection.main;
2 import org.loon.game.simple.alldirection.GameCursor;
3 import org.loon.game.simple.alldirection.GameFrame;
4 import org.loon.game.simple.alldirection.rpg.Config;
5 import org.loon.game.simple.alldirection.rpg.Role;
6 import org.loon.game.simple.alldirection.rpg.RpgLayout;
7 import org.loon.game.simple.alldirection.rpg.RpgMap;
8 /**
9 * Copyright 2008 - 2009
10 *
11 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
12 * use this file except in compliance with the License. You may obtain a copy of
13 * the License at
14 *
15 * http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
19 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
20 * License for the specific language governing permissions and limitations under
21 * the License.
22 *
23 * @project loonframework
24 * @author chenpeng
25 * @email:ceponline@yahoo.com.cn
26 * @version 0.1
27 */
28 public class Main {
29     public static void main(String[] args) {
30         java.awt.EventQueue.invokeLater(new Runnable() {
31             public void run() {
32                 GameFrame frame = new GameFrame(
33                         "Java 2.5D游戏开发中的八方走法实现<LoonFramework-Game>", 640, 480);
34                 // 设定游标
35                 frame.setCursor(GameCursor.getCursor("image/cursor.png"));
36                 // 游戏地图
37                 RpgMap rpgMap = new RpgMap("image/map/maze.jpg",
38                         "./image/map/maze.map");
39                 // 显示网格
40                  rpgMap.showGrid(false);
41                 // 创建主角
42                 Role hero = new Role("image/role/gm.png", 7, 8, Config.DOWN, 0,
43                         rpgMap);
44                 hero.setName("妈妈说坏孩子长大以后就是GM");
45                 hero.setPartyName("曾经的北S商人");
46                 // 创建NPC1
47                 Role npc1 = new Role("image/role/assassin.png", 3, 14,
48                         Config.LEFT, 1, rpgMap);
49                 npc1.setName("炼妖狐(爆刺、爆刺、一拍即死)");
50                 npc1.setPartyName("此人已死,有事烧纸");
51                 // //创建NPC2
52                 Role npc2 = new Role("image/role/rogue.png", 15, 21, Config.LEFT,
53                         1, rpgMap);
54                 npc2.setName("猫猫(巴帽小偷)");
55                 npc2.setPartyName("病猫不发威你还拿我当老虎了");
56                 // 设定主角
57                 rpgMap.setupHero(hero);
58                 // 设定NPC
59                 rpgMap.addRole(npc1);
60                 rpgMap.addRole(npc2);
61                 frame.getGame().setControl(new RpgLayout(rpgMap));
62                 // 游戏全屏
63                 // frame.updateFullScreen();
64                 frame.setFPS(true);
65                 frame.mainLoop();
66                 frame.showFrame();
67             }
68         });
69     }
70 }

  示例截取图如下所示:

 

 

        本回提供的源码内容包括:八方走法(支持鼠标及键盘)、地图移动、角色碰撞、NPC随机行动,详细请下载参看. 

       至于脚本处理、事件触发,对话框、场景转换、角色对战等部分将在以后逐步讲解。

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

 
0
相关文章