我们都知道,在诸如魔兽的很多战略游戏中,会出现要求某A角色接近某B角色的情况(攻击或是怎么着咱不考虑……),玩家很简单的一步操作,却会引发开发者“怎么走过去”这个“复杂”的算法问题,也就是所谓的“Path-finding”——寻径。
好的寻径算法,不但能避免不必要的资源损耗,而且能令游戏友好度增加。反之则会严重影响游戏的可玩性或诸如GIS等系统的可靠性。
在本次,我以Java为例,演示一个最简单的寻径系统——甚至可以说是伪寻径系统的实现。 (^^)
首先,我们建立一个追踪者(一说到这里我就想起了生化里的掠食者……)
而后,我们建立一个被追踪的目标。
最后,我们建立一个面板,用以绘制图形。
这时,画面上显示的红色正方体为追踪者,蓝色圆球为被追踪目标,而无论篮球如何移动,红色正方体都将始终向篮球靠拢。
实际上,通过代码我们就可以知道,这时两者间的移动路径,被没有经过复杂的方法演算,而是以目标的坐标来决定的,颇有独孤九剑那种,敌人若是只有一招,我也只有一招,他若是有千招万招,我也自是千招万招。但是,这种方法在实际的寻径处理中,并不能很好的解决如障碍物,区域转换等问题,所以只是一种[伪]寻径,
或者说是在无障碍情况下的简单解决方案。
如果关注寻径算法在Java中的进一步实现,还请继续关注我的blog……
好的寻径算法,不但能避免不必要的资源损耗,而且能令游戏友好度增加。反之则会严重影响游戏的可玩性或诸如GIS等系统的可靠性。
在本次,我以Java为例,演示一个最简单的寻径系统——甚至可以说是伪寻径系统的实现。 (^^)
首先,我们建立一个追踪者(一说到这里我就想起了生化里的掠食者……)
package
org.test.tracker;
import java.awt.Color;
import java.awt.Graphics;
/***/ /**
*<p>
*Title:LoonFramework
*</p>
*<p>
*Description:追踪者。
*</p>
*<p>
*Copyright:Copyright(c)2007
*</p>
*<p>
*Company:LoonFramework
*</p>
*
*@authorchenpeng
*@email:ceponline@yahoo.com.cn
*@version0.1
*/
public class Tracker ... {
privatestaticfinalintSIZE=TrackerPanel.SIZE;
publicintx;
publicinty;
/***//**
*析构函数,默认坐标为0,0
*
*/
publicTracker()...{
this(0,0);
}
/***//**
*析构函数,设定x,y坐标
*
*@paramx
*@paramy
*/
publicTracker(intx,inty)...{
this.x=x;
this.y=y;
}
/***//**
*设定追踪对象
*
*@paramprey
*/
publicvoidchase(Objectiveobjective)...{
//始终以目标坐标矫正追踪者坐标。
if(x>objective.x)...{
x--;
}elseif(x<objective.x)...{
x++;
}
if(y>objective.y)...{
y--;
}elseif(y<objective.y)...{
y++;
}
}
/***//**
*绘制追踪者
*
*@paramg
*/
publicvoiddraw(Graphicsg)...{
g.setColor(Color.RED);
g.fillRect(x*SIZE,y*SIZE,SIZE,SIZE);
}
}
import java.awt.Color;
import java.awt.Graphics;
/***/ /**
*<p>
*Title:LoonFramework
*</p>
*<p>
*Description:追踪者。
*</p>
*<p>
*Copyright:Copyright(c)2007
*</p>
*<p>
*Company:LoonFramework
*</p>
*
*@authorchenpeng
*@email:ceponline@yahoo.com.cn
*@version0.1
*/
public class Tracker ... {
privatestaticfinalintSIZE=TrackerPanel.SIZE;
publicintx;
publicinty;
/***//**
*析构函数,默认坐标为0,0
*
*/
publicTracker()...{
this(0,0);
}
/***//**
*析构函数,设定x,y坐标
*
*@paramx
*@paramy
*/
publicTracker(intx,inty)...{
this.x=x;
this.y=y;
}
/***//**
*设定追踪对象
*
*@paramprey
*/
publicvoidchase(Objectiveobjective)...{
//始终以目标坐标矫正追踪者坐标。
if(x>objective.x)...{
x--;
}elseif(x<objective.x)...{
x++;
}
if(y>objective.y)...{
y--;
}elseif(y<objective.y)...{
y++;
}
}
/***//**
*绘制追踪者
*
*@paramg
*/
publicvoiddraw(Graphicsg)...{
g.setColor(Color.RED);
g.fillRect(x*SIZE,y*SIZE,SIZE,SIZE);
}
}
而后,我们建立一个被追踪的目标。
package
org.test.tracker;
import java.awt.Color;
import java.awt.Graphics;
/***/ /**
*<p>
*Title:LoonFramework
*</p>
*<p>
*Description:被跟踪的目标对象。
*</p>
*<p>
*Copyright:Copyright(c)2007
*</p>
*<p>
*Company:LoonFramework
*</p>
*
*@authorchenpeng
*@email:ceponline@yahoo.com.cn
*@version0.1
*/
public class Objective ... {
privatestaticfinalintSIZE=TrackerPanel.SIZE;
privatestaticfinalintUP=0;
privatestaticfinalintDOWN=1;
privatestaticfinalintLEFT=2;
privatestaticfinalintRIGHT=3;
publicintx;
publicinty;
/***//**
*析构函数,内部转换x,y坐标。
*
*/
publicObjective()...{
x=TrackerPanel.COL/2;
y=TrackerPanel.ROW/2;
}
/***//**
*析构函数,设定x,y坐标。
*
*/
publicObjective(intx,inty)...{
this.x=x;
this.y=y;
}
/***//**
*移动目标
*
*@paramdir
*/
publicvoidmove(intdir)...{
switch(dir)...{
caseUP:
y--;
break;
caseDOWN:
y++;
break;
caseLEFT:
x--;
break;
caseRIGHT:
x++;
break;
}
}
/***//**
*绘制目标
*
*@paramg
*/
publicvoiddraw(Graphicsg)...{
g.setColor(Color.BLUE);
g.fillOval(x*SIZE,y*SIZE,SIZE,SIZE);
}
}
import java.awt.Color;
import java.awt.Graphics;
/***/ /**
*<p>
*Title:LoonFramework
*</p>
*<p>
*Description:被跟踪的目标对象。
*</p>
*<p>
*Copyright:Copyright(c)2007
*</p>
*<p>
*Company:LoonFramework
*</p>
*
*@authorchenpeng
*@email:ceponline@yahoo.com.cn
*@version0.1
*/
public class Objective ... {
privatestaticfinalintSIZE=TrackerPanel.SIZE;
privatestaticfinalintUP=0;
privatestaticfinalintDOWN=1;
privatestaticfinalintLEFT=2;
privatestaticfinalintRIGHT=3;
publicintx;
publicinty;
/***//**
*析构函数,内部转换x,y坐标。
*
*/
publicObjective()...{
x=TrackerPanel.COL/2;
y=TrackerPanel.ROW/2;
}
/***//**
*析构函数,设定x,y坐标。
*
*/
publicObjective(intx,inty)...{
this.x=x;
this.y=y;
}
/***//**
*移动目标
*
*@paramdir
*/
publicvoidmove(intdir)...{
switch(dir)...{
caseUP:
y--;
break;
caseDOWN:
y++;
break;
caseLEFT:
x--;
break;
caseRIGHT:
x++;
break;
}
}
/***//**
*绘制目标
*
*@paramg
*/
publicvoiddraw(Graphicsg)...{
g.setColor(Color.BLUE);
g.fillOval(x*SIZE,y*SIZE,SIZE,SIZE);
}
}
最后,我们建立一个面板,用以绘制图形。
package
org.test.tracker;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Panel;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
/***/ /**
*<p>
*Title:LoonFramework
*</p>
*<p>
*Description:追踪演示面板。
*</p>
*<p>
*Copyright:Copyright(c)2007
*</p>
*<p>
*Company:LoonFramework
*</p>
*
*@authorchenpeng
*@email:ceponline@yahoo.com.cn
*@version0.1
*/
public class TrackerPanel extends Panel implements Runnable,KeyListener ... {
/***//**
*
*/
privatestaticfinallongserialVersionUID=1L;
//设定窗体宽与高
privatestaticfinalintWIDTH=480;
privatestaticfinalintHEIGHT=480;
//描绘的正方体大小
staticfinalintSIZE=8;
//获得对应列数,即将frame转化为类数组的存在。
publicstaticfinalintROW=HEIGHT/SIZE;
publicstaticfinalintCOL=WIDTH/SIZE;
//获得对应行数
privatestaticfinalintUP=0;
privatestaticfinalintDOWN=1;
privatestaticfinalintLEFT=2;
privatestaticfinalintRIGHT=3;
//追踪者
privateTrackertracker;
//目标
privateObjectiveobjective;
privateThreadthread;
privateImagescreen=null;
privateGraphicsbg=null;
publicTrackerPanel()...{
setPreferredSize(newDimension(WIDTH,HEIGHT));
setFocusable(true);
addKeyListener(this);
screen=newBufferedImage(WIDTH,HEIGHT,1);
bg=screen.getGraphics();
//追踪者初始位置,0行,0列
tracker=newTracker(0,0);
//目标初始位置,50行,50列
objective=newObjective(50,50);
thread=newThread(this);
thread.start();
}
publicvoidpaint(Graphicsg)...{
//设定底色为白色,并清屏
bg.setColor(Color.white);
bg.fillRect(0,0,WIDTH,HEIGHT);
//绘制追踪者
tracker.draw(bg);
//绘制目标
objective.draw(bg);
g.drawImage(screen,0,0,this);
g.dispose();
}
publicvoidupdate(Graphicsg)...{
paint(g);
}
publicvoidrun()...{
while(true)...{
//设定追击的目标。
tracker.chase(objective);
repaint();
try...{
Thread.sleep(110);
}catch(InterruptedExceptione)...{
e.printStackTrace();
}
}
}
publicvoidkeyTyped(KeyEvente)...{
}
publicvoidkeyPressed(KeyEvente)...{
intkey=e.getKeyCode();
switch(key)...{
caseKeyEvent.VK_UP:
objective.move(UP);
break;
caseKeyEvent.VK_DOWN:
objective.move(DOWN);
break;
caseKeyEvent.VK_LEFT:
objective.move(LEFT);
break;
caseKeyEvent.VK_RIGHT:
objective.move(RIGHT);
break;
}
repaint();
}
publicvoidkeyReleased(KeyEvente)...{
}
publicstaticvoidmain(String[]args)...{
Framefrm=newFrame();
frm.setTitle("简单的Java图形寻径追踪实现(由Loonframework提供)");
frm.setSize(WIDTH,HEIGHT);
frm.setResizable(false);
frm.add(newTrackerPanel());
frm.setVisible(true);
}
}
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Panel;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
/***/ /**
*<p>
*Title:LoonFramework
*</p>
*<p>
*Description:追踪演示面板。
*</p>
*<p>
*Copyright:Copyright(c)2007
*</p>
*<p>
*Company:LoonFramework
*</p>
*
*@authorchenpeng
*@email:ceponline@yahoo.com.cn
*@version0.1
*/
public class TrackerPanel extends Panel implements Runnable,KeyListener ... {
/***//**
*
*/
privatestaticfinallongserialVersionUID=1L;
//设定窗体宽与高
privatestaticfinalintWIDTH=480;
privatestaticfinalintHEIGHT=480;
//描绘的正方体大小
staticfinalintSIZE=8;
//获得对应列数,即将frame转化为类数组的存在。
publicstaticfinalintROW=HEIGHT/SIZE;
publicstaticfinalintCOL=WIDTH/SIZE;
//获得对应行数
privatestaticfinalintUP=0;
privatestaticfinalintDOWN=1;
privatestaticfinalintLEFT=2;
privatestaticfinalintRIGHT=3;
//追踪者
privateTrackertracker;
//目标
privateObjectiveobjective;
privateThreadthread;
privateImagescreen=null;
privateGraphicsbg=null;
publicTrackerPanel()...{
setPreferredSize(newDimension(WIDTH,HEIGHT));
setFocusable(true);
addKeyListener(this);
screen=newBufferedImage(WIDTH,HEIGHT,1);
bg=screen.getGraphics();
//追踪者初始位置,0行,0列
tracker=newTracker(0,0);
//目标初始位置,50行,50列
objective=newObjective(50,50);
thread=newThread(this);
thread.start();
}
publicvoidpaint(Graphicsg)...{
//设定底色为白色,并清屏
bg.setColor(Color.white);
bg.fillRect(0,0,WIDTH,HEIGHT);
//绘制追踪者
tracker.draw(bg);
//绘制目标
objective.draw(bg);
g.drawImage(screen,0,0,this);
g.dispose();
}
publicvoidupdate(Graphicsg)...{
paint(g);
}
publicvoidrun()...{
while(true)...{
//设定追击的目标。
tracker.chase(objective);
repaint();
try...{
Thread.sleep(110);
}catch(InterruptedExceptione)...{
e.printStackTrace();
}
}
}
publicvoidkeyTyped(KeyEvente)...{
}
publicvoidkeyPressed(KeyEvente)...{
intkey=e.getKeyCode();
switch(key)...{
caseKeyEvent.VK_UP:
objective.move(UP);
break;
caseKeyEvent.VK_DOWN:
objective.move(DOWN);
break;
caseKeyEvent.VK_LEFT:
objective.move(LEFT);
break;
caseKeyEvent.VK_RIGHT:
objective.move(RIGHT);
break;
}
repaint();
}
publicvoidkeyReleased(KeyEvente)...{
}
publicstaticvoidmain(String[]args)...{
Framefrm=newFrame();
frm.setTitle("简单的Java图形寻径追踪实现(由Loonframework提供)");
frm.setSize(WIDTH,HEIGHT);
frm.setResizable(false);
frm.add(newTrackerPanel());
frm.setVisible(true);
}
}
这时,画面上显示的红色正方体为追踪者,蓝色圆球为被追踪目标,而无论篮球如何移动,红色正方体都将始终向篮球靠拢。
实际上,通过代码我们就可以知道,这时两者间的移动路径,被没有经过复杂的方法演算,而是以目标的坐标来决定的,颇有独孤九剑那种,敌人若是只有一招,我也只有一招,他若是有千招万招,我也自是千招万招。但是,这种方法在实际的寻径处理中,并不能很好的解决如障碍物,区域转换等问题,所以只是一种[伪]寻径,
或者说是在无障碍情况下的简单解决方案。
如果关注寻径算法在Java中的进一步实现,还请继续关注我的blog……