AVG,即Adventure Game,可以直译为[冒险游戏]。但是通常情况下我们说AVG是指[文字冒险游戏],也有人更直白的解释成自己选择路线和结局的电子小说,与硬砍硬杀的RPG或者揉破键盘的ACT不同,AVG多以解谜或文字游戏等脑力攻关推动剧情发展。现在日本流行的ADV,可以看作是AVG英文全称的不同缩写方式,大体上讲,AVG == ADV。
 
 由于商业化需要,现代主流的AVG往往是GalGame,也就是少女游戏,或称少女恋爱游戏,但GalGame != AVG,只是下属分支中的一环罢了,AVG包含GalGame,但GalGame并不能完全代表AVG/ADV。另外关于GalGame的详细介绍,在若木民喜《只有神才知道的世界》中演绎的相当生动,有兴趣的可以自己去看看~
 
  Java版AVG游戏开发入门[0]——游戏模式转换中的事件交互_第1张图片
 
   就技术角度而言, AVG 开发可以算得所有游戏类型中最容易的。一款简单 AVG 游戏的制作难度甚至在贪食蛇、俄罗斯方块之下。由于实现的简易性,导致 AVG 的开发重心往往着重于策划及美工,程序员的作用则微乎其微。同时也正因 AVG 开发的门坎约等于 0 ,所以此类型的同人游戏之多即可堪称世界之冠。另外, AVG 开发工具普及的也促进了 AVG 的量产化。利用工具,即始是小说作者、漫画家等非软件专业出身的人士,往往也能轻易制作出顶级的 AVG 大作。(顺便一提,目前我所见过最好的 AVG 制作工具是鬼子的 livemaker ,采用类似思维导图的方式构造整个游戏,很多轻小说作者乃至网络漫画家用它制作自己作品的宣传游戏。但就技术角度上说, livemaker 的开发依旧没什么难度 ......
 
  由于 AVG 的大泛滥,通常仅有文字、图片及语音的 AVG 往往无法满足用户需求( H 除外-_-)。我们每每可在 AVG 游戏类型后发现个 + 号,比如《樱花大战》是 AVG+SLG ,《生化危机》是 AVG+ACT 。所以客观上说, AVG 开发仅仅能进行字图的交互是不够的,还要解决多模块组件的协调问题。
 
  Java 桌面应用开发中 , 我们都知道绘图是极为简单的,有 Image Graphics 两个对象就可以 Paint 一个图形,即使图形对象再多,最后它们也必须统一在一个 Paint 中,所以 Java 中不存在图像的交互问题。
 
但问题在于,图像的显示可以统一,但是触发图像变化的事件却是很难统一的。比如现在有需求如下,在 AVG 模式中,触发键盘事件上、下、左、右时为控制画面的前进、后退,切换模式到 SLG 模 式后,设定上、下、左、右是光标移动,那么如果我要在程序中实现,就必须记录当前模式,而后根据不同模式调用事件,再反馈到图形上。如果只有几个事件的区 别,我们当然可以很容易用分支来实现;问题是,随着游戏规模的加大,这些分支将成几何倍数增多,单纯的分支判定到最后只能忙于应付,落个费力不讨好。
 
其实在这时,我们大可以使用一些技巧来轻松解决问题。
 
示例如下:
 
 
首先,我们构造一个接口,命名为IControl,继承鼠标及键盘监听,并在其中设定两个抽象方法:
 
   
view plain copy to clipboard print ?
  1. package org.loon.simple.avg;  
  2. import java.awt.Graphics;  
  3. import java.awt.event.KeyListener;  
  4. import java.awt.event.MouseListener;  
  5. import java.awt.event.MouseMotionListener;  
  6. /** 
  7.  * Copyright 2008 - 2009 
  8.  *  
  9.  * Licensed under the Apache License, Version 2.0 (the "License"); you may not 
  10.  * use this file except in compliance with the License. You may obtain a copy of 
  11.  * the License at 
  12.  *  
  13.  * [url]http://www.apache.org/licenses/LICENSE-2.0[/url] 
  14.  *  
  15.  * Unless required by applicable law or agreed to in writing, software 
  16.  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 
  17.  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 
  18.  * License for the specific language governing permissions and limitations under 
  19.  * the License. 
  20.  *  
  21.  * @project loonframework 
  22.  * @author chenpeng 
  23.  * @email:[email][email protected][/email] 
  24.  * @version 0.1 
  25.  */  
  26. public interface IControl extends MouseListener, MouseMotionListener,  
  27.         KeyListener {  
  28.     public abstract void draw(final Graphics g);  
  29.     public abstract IControl invoke();  
  30. }  
package org.loon.simple.avg; import java.awt.Graphics; import java.awt.event.KeyListener; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; /** * Copyright 2008 - 2009 * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * [url]http://www.apache.org/licenses/LICENSE-2.0[/url] * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed . an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * * @project loonframework * @author chenpeng * @email:[email][email protected][/email] * @version 0.1 */ public interface IControl extends MouseListener, MouseMotionListener, KeyListener { public abstract void draw(final Graphics g); public abstract IControl invoke(); }
 
 
  而后,再构造一个接口,命名为IAVG,同样继承鼠标及键盘监听,并在其中设定三个抽象方法,用以操作IControl接口:

   
view plain copy to clipboard print ?
  1. package org.loon.simple.avg;  
  2. import java.awt.Graphics;  
  3. import java.awt.event.KeyListener;  
  4. import java.awt.event.MouseListener;  
  5. import java.awt.event.MouseMotionListener;  
  6. /** 
  7.  * Copyright 2008 - 2009 
  8.  *  
  9.  * Licensed under the Apache License, Version 2.0 (the "License"); you may not 
  10.  * use this file except in compliance with the License. You may obtain a copy of 
  11.  * the License at 
  12.  *  
  13.  * [url]http://www.apache.org/licenses/LICENSE-2.0[/url] 
  14.  *  
  15.  * Unless required by applicable law or agreed to in writing, software 
  16.  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 
  17.  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 
  18.  * License for the specific language governing permissions and limitations under 
  19.  * the License. 
  20.  *  
  21.  * @project loonframework 
  22.  * @author chenpeng 
  23.  * @email:[email][email protected][/email] 
  24.  * @version 0.1 
  25.  */  
  26. public interface IAVG extends MouseListener, MouseMotionListener,  
  27.         KeyListener {  
  28.     public abstract void draw(final Graphics g);  
  29.     public abstract IControl getControl();  
  30.     public abstract void setControl(final IControl control);  
  31. }   
package org.loon.simple.avg; import java.awt.Graphics; import java.awt.event.KeyListener; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; /** * Copyright 2008 - 2009 * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * [url]http://www.apache.org/licenses/LICENSE-2.0[/url] * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed . an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * * @project loonframework * @author chenpeng * @email:[email][email protected][/email] * @version 0.1 */ public interface IAVG extends MouseListener, MouseMotionListener, KeyListener { public abstract void draw(final Graphics g); public abstract IControl getControl(); public abstract void setControl(final IControl control); } 

     再后,制作一个显示图像用组件,命名为AVGCanva,继承自Canvas。
 
    
view plain copy to clipboard print ?
  1. package org.loon.simple.avg;  
  2. import java.awt.Canvas;  
  3. import java.awt.Graphics;  
  4. /** 
  5.  * Copyright 2008 - 2009 
  6.  *  
  7.  * Licensed under the Apache License, Version 2.0 (the "License"); you may not 
  8.  * use this file except in compliance with the License. You may obtain a copy of 
  9.  * the License at 
  10.  *  
  11.  * [url]http://www.apache.org/licenses/LICENSE-2.0[/url] 
  12.  *  
  13.  * Unless required by applicable law or agreed to in writing, software 
  14.  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 
  15.  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 
  16.  * License for the specific language governing permissions and limitations under 
  17.  * the License. 
  18.  *  
  19.  * @project loonframework 
  20.  * @author chenpeng 
  21.  * @email:[email][email protected][/email] 
  22.  * @version 0.1 
  23.  */  
  24. public class AVGCanvas extends Canvas {  
  25.     /** 
  26.      *  
  27.      */  
  28.     private static final long serialVersionUID = 1982278682597393958L;  
  29.     private boolean start;  
  30.     private IAVG avg;  
  31.     public AVGCanvas(IAVG handler) {  
  32.         this.avg = handler;  
  33.         this.start = false;  
  34.         this.addKeyListener(handler);  
  35.         this.addMouseListener(handler);  
  36.         this.addMouseMotionListener(handler);  
  37.     }  
  38.       
  39.     public void update(Graphics g) {  
  40.         paint(g);  
  41.     }  
  42.     public void paint(Graphics g) {  
  43.         if (this.start) {  
  44.             this.avg.draw(g);  
  45.         }  
  46.     }  
  47.     public void startPaint() {  
  48.         this.start = true;  
  49.     }  
  50.     public void endPaint() {  
  51.         this.start = false;  
  52.     }  
  53. }  
package org.loon.simple.avg; import java.awt.Canvas; import java.awt.Graphics; /** * Copyright 2008 - 2009 * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * [url]http://www.apache.org/licenses/LICENSE-2.0[/url] * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed . an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * * @project loonframework * @author chenpeng * @email:[email][email protected][/email] * @version 0.1 */ public class AVGCanvas extends Canvas { /** * */ private static final long serialVersionUID = 1982278682597393958L; private boolean start; private IAVG avg; public AVGCanvas(IAVG handler) { this.avg = handler; this.start = false; this.addKeyListener(handler); this.addMouseListener(handler); this.addMouseMotionListener(handler); } public void update(Graphics g) { paint(g); } public void paint(Graphics g) { if (this.start) { this.avg.draw(g); } } public void startPaint() { this.start = true; } public void endPaint() { this.start = false; } }
 
     这段代码中的paint方法中并没有现成的方法,而是调用了IAVG接口的draw。紧接着,我们再设定一个AVGFrame用以加载AVGCanvas。
 
   
view plain copy to clipboard print ?
  1. package org.loon.simple.avg;  
  2. import java.awt.Color;  
  3. import java.awt.Dimension;  
  4. import java.awt.Frame;  
  5. import java.awt.event.WindowAdapter;  
  6. import java.awt.event.WindowEvent;  
  7.   
  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.  * [url]http://www.apache.org/licenses/LICENSE-2.0[/url] 
  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:[email][email protected][/email] 
  26.  * @version 0.1 
  27.  */  
  28. public class AVGFrame extends Frame implements Runnable {  
  29.     /** 
  30.      *  
  31.      */  
  32.     private static final long serialVersionUID = 198284399945549558L;  
  33.     private IAVG avg;  
  34.     private AVGCanvas canvas;  
  35.     private boolean fps;  
  36.     private String titleName;  
  37.     private Thread mainLoop;  
  38.     public AVGFrame(String titleName, int width, int height) {  
  39.         this(new AVG(), titleName, width, height);  
  40.     }  
  41.     public AVGFrame(IAVG avg, String titleName, int width, int height) {  
  42.         super(titleName);  
  43.         Lib.WIDTH = width;  
  44.         Lib.HEIGHT = height;  
  45.         this.avg = avg;  
  46.         this.titleName = titleName;  
  47.         this.addKeyListener(avg);  
  48.         this.setPreferredSize(new Dimension(width + 5, height + 25));  
  49.         this.initCanvas(Lib.WIDTH, Lib.HEIGHT);  
  50.         this.pack();  
  51.         this.addWindowListener(new WindowAdapter() {  
  52.             public void windowClosing(WindowEvent e) {  
  53.                 System.exit(0);  
  54.             }  
  55.         });  
  56.         this.setResizable(false);  
  57.         this.setLocationRelativeTo(null);  
  58.         this.setVisible(true);  
  59.     }  
  60.     public void run() {  
  61.         gameLoop();  
  62.     }  
  63.     /** 
  64.      * 开始循环窗体图像 
  65.      *  
  66.      */  
  67.     private synchronized void gameLoop() {  
  68.         canvas.startPaint();  
  69.         long second = 0L;  
  70.         int moveCount = 0;  
  71.         // 循环绘制  
  72.         for (;;) {  
  73.             long start = System.currentTimeMillis();  
  74.             this.paintScreen();  
  75.             long end = System.currentTimeMillis();  
  76.             long time = end - start;  
  77.             long sleepTime = 20L - time;  
  78.             if (sleepTime < 0L)  
  79.                 sleepTime = 0L;  
  80.             try {  
  81.                 Thread.sleep(sleepTime);  
  82.             } catch (InterruptedException e) {  
  83.                 e.printStackTrace();  
  84.             }  
  85.             if (this.fps) {  
  86.                 moveCount++;  
  87.                 second += System.currentTimeMillis() - start;  
  88.                 if (second >= 1000L) {  
  89.                     this.setTitle(new StringBuilder(titleName).append(" FPS:")  
  90.                             .append(moveCount).toString());  
  91.                     moveCount = 0;  
  92.                     second = 0L;  
  93.                 }  
  94.             }  
  95.         }  
  96.     }  
  97.     /** 
  98.      * 启动游戏循环 
  99.      *  
  100.      */  
  101.     public void mainLoop() {  
  102.         this.mainLoop = new Thread(this);  
  103.         this.mainLoop.start();  
  104.     }  
  105.     /** 
  106.      * 初始化背景帆布 
  107.      *  
  108.      * @param width 
  109.      * @param height 
  110.      */  
  111.     private void initCanvas(final int width, final int height) {  
  112.         canvas = new AVGCanvas(avg);  
  113.         canvas.setBackground(Color.black);  
  114.         canvas.setPreferredSize(new Dimension(width, height));  
  115.         this.add(canvas);  
  116.     }  
  117.     public IAVG getAVG() {  
  118.         return this.avg;  
  119.     }  
  120.     protected void processWindowEvent(WindowEvent e) {  
  121.         super.processWindowEvent(e);  
  122.     }  
  123.     public synchronized void paintScreen() {  
  124.         canvas.repaint();  
  125.     }  
  126.     public boolean isShowFPS() {  
  127.         return fps;  
  128.     }  
  129.     public void setShowFPS(boolean fps) {  
  130.         this.fps = fps;  
  131.     }  
  132.     public Thread getMainLoop() {  
  133.         return mainLoop;  
  134.     }  
  135.     public String getTitleName() {  
  136.         return titleName;  
  137.     }  
  138. }  
package org.loon.simple.avg; import java.awt.Color; import java.awt.Dimension; import java.awt.Frame; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; /** * Copyright 2008 - 2009 * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * [url]http://www.apache.org/licenses/LICENSE-2.0[/url] * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed . an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * * @project loonframework * @author chenpeng * @email:[email][email protected][/email] * @version 0.1 */ public class AVGFrame extends Frame implements Runnable { /** * */ private static final long serialVersionUID = 198284399945549558L; private IAVG avg; private AVGCanvas canvas; private boolean fps; private String titleName; private Thread mainLoop; public AVGFrame(String titleName, int width, int height) { this(new AVG(), titleName, width, height); } public AVGFrame(IAVG avg, String titleName, int width, int height) { super(titleName); Lib.WIDTH = width; Lib.HEIGHT = height; this.avg = avg; this.titleName = titleName; this.addKeyListener(avg); this.setPreferredSize(new Dimension(width + 5, height + 25)); this.initCanvas(Lib.WIDTH, Lib.HEIGHT); this.pack(); this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); this.setResizable(false); this.setLocationRelativeTo(null); this.setVisible(true); } public void run() { gameLoop(); } /** * 开始循环窗体图像 * */ private synchronized void gameLoop() { canvas.startPaint(); long second = 0L; int moveCount = 0; // 循环绘制 for (;;) { long start = System.currentTimeMillis(); this.paintScreen(); long end = System.currentTimeMillis(); long time = end - start; long sleepTime = 20L - time; if (sleepTime < 0L) sleepTime = 0L; try { Thread.sleep(sleepTime); } catch (InterruptedException e) { e.printStackTrace(); } if (this.fps) { moveCount++; second += System.currentTimeMillis() - start; if (second >= 1000L) { this.setTitle(new StringBuilder(titleName).append(" FPS:") .append(moveCount).toString()); moveCount = 0; second = 0L; } } } } /** * 启动游戏循环 * */ public void mainLoop() { this.mainLoop = new Thread(this); this.mainLoop.start(); } /** * 初始化背景帆布 * * @param width * @param height */ private void initCanvas(final int width, final int height) { canvas = new AVGCanvas(avg); canvas.setBackground(Color.black); canvas.setPreferredSize(new Dimension(width, height)); this.add(canvas); } public IAVG getAVG() { return this.avg; } protected void processWindowEvent(WindowEvent e) { super.processWindowEvent(e); } public synchronized void paintScreen() { canvas.repaint(); } public boolean isShowFPS() { return fps; } public void setShowFPS(boolean fps) { this.fps = fps; } public Thread getMainLoop() { return mainLoop; } public String getTitleName() { return titleName; } }
 
  我们可以看到,在本例鼠标键盘事件及图像绘制完全通过接口方式实现。 此时,只要让不同组件统一实现IControl接口,便可以轻松转换事件及图像的绘制。也正是我们都再熟悉不过的 MVC 模式中,通过 Event 导致 Controller 改变 Model View 的基本原理。
 
   下一回,我们将具体讲解一个AVG游戏实现的基本流程。
 
  示例代码界面如下图:
 
  Java版AVG游戏开发入门[0]——游戏模式转换中的事件交互_第2张图片
 
 
  Java版AVG游戏开发入门[0]——游戏模式转换中的事件交互_第3张图片
 
  Java版AVG游戏开发入门[0]——游戏模式转换中的事件交互_第4张图片
 
  Java版AVG游戏开发入门[0]——游戏模式转换中的事件交互_第5张图片
 
  Java版AVG游戏开发入门[0]——游戏模式转换中的事件交互_第6张图片


示例程序下载地址:[url]http://download.csdn.net/source/×××73[/url](源码在jar内)