源码下载:
http://download.csdn.net/source/405250
Java的图像处理能力相对较弱是一个不争的事实,因为jre需要兼顾不同系统间的相同功能实现,所以并非所有图形操作都可以利用java进行。但对于绝大多数的图形开发而言,java已经足够强大了,尤其是对2d图形游戏而言,其简单快捷的编码风格在某些时候完全可以应用到实际的游戏开发中去。
在以前的blog文章中,我曾经历居过一些简单的实例,此系列中我会进行比以前更深入的用例讲解。
以前的文章中,我总是喜欢以窗体模式的frame显示数据,这样做有例有弊。好处在于不会消耗额外的系统资源,而坏处在于若不进行相关代码调整则窗体内图像大小总是固定的,无论显示器实际分辨率如何窗体中图形总是会以默认大小显示,而且在窗体环绕中的游戏临场感也不够强。
实际上Java中的frame是可以转化为全屏方式显示的,只是我们善于利用GraphicsEnvironment类,就可以对系统显示环境作相应的调整操作。
ScreenManager.java
package
org.loon.game.test;

import
java.awt.Color;
import
java.awt.DisplayMode;
import
java.awt.EventQueue;
import
java.awt.Frame;
import
java.awt.Graphics2D;
import
java.awt.GraphicsConfiguration;
import
java.awt.GraphicsDevice;
import
java.awt.GraphicsEnvironment;
import
java.awt.Toolkit;
import
java.awt.Window;
import
java.awt.event.WindowAdapter;
import
java.awt.event.WindowEvent;
import
java.awt.image.BufferStrategy;
import
java.awt.image.BufferedImage;
import
java.lang.reflect.InvocationTargetException;

/***/
/**
*
*<p>Title:LoonFramework</p>
*<p>Description:用于进行屏幕管理</p>
*<p>Copyright:Copyright(c)2008</p>
*<p>Company:LoonFramework</p>
*<p>License:http://www.apache.org/licenses/LICENSE-2.0</p>
*@authorchenpeng
*@email:[email protected]
*@version0.1
*/

public
class
ScreenManager
...
{

privateGraphicsDevicedevice;


publicScreenManager()...{
GraphicsEnvironmentenvironment=GraphicsEnvironment
.getLocalGraphicsEnvironment();
device=environment.getDefaultScreenDevice();
}


/***//**
*返回系统支持的显示模式数组
*
*@return
*/

publicDisplayMode[]getCompatibleDisplayModes()...{
returndevice.getDisplayModes();
}


/***//**
*返回与指定数组兼容的显示模式清单
*
*@parammodes
*@return
*/

publicDisplayModefindFirstCompatibleMode(DisplayModemodes[])...{
DisplayModegoodModes[]=device.getDisplayModes();

for(inti=0;i<modes.length;i++)...{

for(intj=0;j<goodModes.length;j++)...{

if(displayModesMatch(modes[i],goodModes[j]))...{
returnmodes[i];
}
}

}

returnnull;
}


/***//**
*返回目前采用的显示模式
*
*@return
*/

publicDisplayModegetCurrentDisplayMode()...{
returndevice.getDisplayMode();
}


/***//**
*匹配两个指定的显示模式
*
*@parammode1
*@parammode2
*@return
*/
publicbooleandisplayModesMatch(DisplayModemode1,DisplayModemode2)


...{
if(mode1.getWidth()!=mode2.getWidth()

||mode1.getHeight()!=mode2.getHeight())...{
returnfalse;
}

if(mode1.getBitDepth()!=DisplayMode.BIT_DEPTH_MULTI
&&mode2.getBitDepth()!=DisplayMode.BIT_DEPTH_MULTI

&&mode1.getBitDepth()!=mode2.getBitDepth())...{
returnfalse;
}

if(mode1.getRefreshRate()!=DisplayMode.REFRESH_RATE_UNKNOWN
&&mode2.getRefreshRate()!=DisplayMode.REFRESH_RATE_UNKNOWN

&&mode1.getRefreshRate()!=mode2.getRefreshRate())...{
returnfalse;
}

returntrue;
}


/***//**
*设置一个默认窗体的全屏模式
*
*@paramdisplayMode
*/

publicvoidsetFullScreen(DisplayModedisplayMode)...{
finalFrameframe=newFrame();
frame.setBackground(Color.BLACK);
setFullScreen(displayMode,frame);
}


/***//**
*设定指定窗体的全屏模式
*
*@paramdisplayMode
*@paramwindow
*/

publicvoidsetFullScreen(DisplayModedisplayMode,finalFramewindow)...{

window.addWindowListener(newWindowAdapter()...{

publicvoidwindowClosing(WindowEvente)...{
System.exit(0);
}
});
window.setUndecorated(true);
window.setResizable(false);
window.setIgnoreRepaint(false);


device.setFullScreenWindow(window);


if(displayMode!=null&&device.isDisplayChangeSupported())...{

try...{
device.setDisplayMode(displayMode);

}catch(IllegalArgumentExceptionex)...{
}
window.setSize(displayMode.getWidth(),displayMode.getHeight());
}

try...{

EventQueue.invokeAndWait(newRunnable()...{

publicvoidrun()...{
window.createBufferStrategy(2);
}
});

}catch(InterruptedExceptionex)...{

}catch(InvocationTargetExceptionex)...{
}
}


/***//**
*取得当前的Graphics2D模式背景
*
*@return
*/

publicGraphics2DgetGraphics()...{
Windowwindow=device.getFullScreenWindow();

if(window!=null)...{
BufferStrategystrategy=window.getBufferStrategy();
return(Graphics2D)strategy.getDrawGraphics();

}else...{
returnnull;
}
}


/***//**
*刷新显示的数据
*
*/

publicvoidupdate()...{
Windowwindow=device.getFullScreenWindow();

if(window!=null)...{
BufferStrategystrategy=window.getBufferStrategy();

if(!strategy.contentsLost())...{
strategy.show();
}
}
//同步
Toolkit.getDefaultToolkit().sync();
}


/***//**
*返回当前窗口
*
*@return
*/

publicFramegetFullScreenWindow()...{
return(Frame)device.getFullScreenWindow();
}


publicintgetWidth()...{
Windowwindow=device.getFullScreenWindow();

if(window!=null)...{
returnwindow.getWidth();

}else...{
return0;
}
}


publicintgetHeight()...{
Windowwindow=device.getFullScreenWindow();

if(window!=null)...{
returnwindow.getHeight();

}else...{
return0;
}
}


/***//**
*恢复屏幕的显示模式
*
*/

publicvoidrestoreScreen()...{
Windowwindow=device.getFullScreenWindow();

if(window!=null)...{
window.dispose();
}
device.setFullScreenWindow(null);
}


/***//**
*创建一个与现有显示模式兼容的bufferedimage
*
*@paramw
*@paramh
*@paramtransparancy
*@return
*/

publicBufferedImagecreateCompatibleImage(intw,inth,inttransparancy)...{
Windowwindow=device.getFullScreenWindow();

if(window!=null)...{
GraphicsConfigurationgc=window.getGraphicsConfiguration();
returngc.createCompatibleImage(w,h,transparancy);
}
returnnull;
}
}
上面我所给出的,是一个Screen的管理类,他集合了对窗体Graphics的操作处理,用例如下。
ScreenTest .java
package
org.loon.game.test;

import
java.awt.Color;
import
java.awt.DisplayMode;
import
java.awt.Font;
import
java.awt.Frame;
import
java.awt.Graphics;



public
class
ScreenTest
extends
Frame
...
{


/***//**
*
*/
privatestaticfinallongserialVersionUID=1L;

privatestaticfinallongTIME=9000;


publicstaticvoidmain(String[]args)...{
//创建一个显示模式及设定参数,分别为:宽、高、比特位数、刷新率(赫兹)
DisplayModedisplayMode=newDisplayMode(800,600,16,
DisplayMode.REFRESH_RATE_UNKNOWN);

ScreenTesttest=newScreenTest();
test.run(displayMode);
}


publicvoidrun(DisplayModedisplayMode)...{
setBackground(Color.black);
setForeground(Color.white);
setFont(newFont("Dialog",0,24));
ScreenManagerscreen=newScreenManager();

try...{
screen.setFullScreen(displayMode,this);

try...{
Thread.sleep(TIME);

}catch(InterruptedExceptionex)...{
}

}finally...{
screen.restoreScreen();
}
}


publicvoidpaint(Graphicsg)...{
g.drawString("HelloWorld!",50,50);
}
}
效果图如下:
我们可以看到,此时一个全屏显示的Hello World被创建出来。以此为基础,我们更可以轻松的创建全屏状态下的动画效果。
Animation.java
package
org.loon.game.test;

import
java.awt.Image;
import
java.util.ArrayList;


public
class
Animation
...
{

//缓存动画面板
privateArrayList_frames;

privateint_frameIndex;

privatelong_time;

privatelong_total;


privateclassAnimationFrame...{

Imageimage;

longendTime;


publicAnimationFrame(Imageimage,longendTime)...{
this.image=image;
this.endTime=endTime;
}
}


publicAnimation()...{
_frames=newArrayList();
_total=0;
start();
}


/***//**
*增加一个指定的动画图片并设置动画时间
*
*@paramimage
*@paramduration
*/

publicsynchronizedvoidaddFrame(Imageimage,longduration)...{
_total+=duration;
_frames.add(newAnimationFrame(image,_total));
}


/***//**
*以默认播放时间载入图像数组
*
*@paramimage
*/

publicvoidaddFrame(Image[]image)...{

for(inti=0;i<image.length;i++)...{
addFrame(image[i],500);
}
}


/***//**
*开始执行动画
*
*/

publicsynchronizedvoidstart()...{
_time=0;
_frameIndex=0;
}


/***//**
*更新此动画播放时间
*
*@paramtime
*/

publicsynchronizedvoidupdate(longtime)...{

if(_frames.size()>1)...{
_time+=time;


if(_time>=_total)...{
_time=_time%_total;
_frameIndex=0;
}


while(_time>getFrame(_frameIndex).endTime)...{
_frameIndex++;
}
}
}


/***//**
*获得当前动画image
*
*@return
*/

publicsynchronizedImagegetImage()...{

if(_frames.size()==0)...{
returnnull;

}else...{
returngetFrame(_frameIndex).image;
}
}


/***//**
*返回指定frame
*
*@parami
*@return
*/

privateAnimationFramegetFrame(inti)...{
return(AnimationFrame)_frames.get(i);
}

}
上面是一个动画面板,用于记录一组连续的画面。
AnimationSimple.java
...
package
org.loon.game.test;

import
java.awt.DisplayMode;
import
java.awt.Graphics;
import
java.awt.Graphics2D;
import
java.awt.Image;
import
java.awt.Toolkit;
import
java.awt.image.BufferedImage;
import
java.awt.image.MemoryImageSource;
import
java.awt.image.PixelGrabber;

import
javax.swing.ImageIcon;


/***/
/**
*
*<p>
*Title:LoonFramework
*</p>
*<p>
*Description:动画演示
*</p>
*<p>
*Copyright:Copyright(c)2008
*</p>
*<p>
*Company:LoonFramework
*</p>
*<p>
*License:http://www.apache.org/licenses/LICENSE-2.0
*</p>
*
*@authorchenpeng
*@email:[email protected]
*@version0.1
*/

public
class
AnimationSimple
...
{
//动作时间
privatestaticfinallongTIME=20000;

finalstaticprivateintWIDTH=800;

finalstaticprivateintHEIGHT=600;

privateScreenManager_screen;

privateImage_bgImage;

privateAnimation_animation1;

privateAnimation_animation2;

privateImage_cacheImage;

privateGraphics_graphics;


publicstaticvoidmain(Stringargs[])...{

//创建一个显示模式及设定参数,分别为:宽、高、比特位数、刷新率(赫兹)PS:frame中图像会自动放大
DisplayModedisplayMode=newDisplayMode(WIDTH,HEIGHT,16,0);
AnimationSimpletest=newAnimationSimple();
test.run(displayMode);

//System.out.println((0<<16)|(0<<8)|0);
}


publicvoidloadImages()...{
//为保持图片同步加载,构建一个cache作为二级缓存
_cacheImage=newBufferedImage(WIDTH,HEIGHT,2);
_graphics=_cacheImage.getGraphics();
//加载图片
_bgImage=loadImage("test_images/bg01.png",false);
Image[]players1=newImage[9];

for(inti=0;i<9;i++)...{
players1[i]=loadImage("test_images/marisa_0"+i+".png",true);
}
Image[]players2=newImage[7];

for(inti=1;i<8;i++)...{
players2[i-1]=loadImage("test_images/reimu2_0"+i+".png",
true);
}
//创建动画
_animation1=newAnimation();
_animation1.addFrame(players1);
_animation2=newAnimation();
_animation2.addFrame(players2);
}


/***//**
*加载图象
*
*@paramfileName
*@paramisfiltration
*@return
*/

privateImageloadImage(StringfileName,booleanisfiltration)...{

//当ImageIcon使用jar包本身资源时,需要通过jvm获得路径
ClassLoaderclassloader=getClass().getClassLoader();
Imageimg=newImageIcon(classloader.getResource(fileName)).getImage();

//为了演示例子没有使用偶的loonframework-game包(那天整合下准备发alpha了),而是直接处理了图像

if(isfiltration)...{
intwidth=img.getWidth(null);
intheight=img.getHeight(null);
//创建一个PixelGrabber
PixelGrabberpg=newPixelGrabber(img,0,0,width,height,true);

try...{
pg.grabPixels();

}catch(InterruptedExceptione)...{
e.printStackTrace();
}
//获取其中像素
intpixels[]=(int[])pg.getPixels();
//遍历过滤像素

for(inti=0;i<pixels.length;i++)...{
//-16777216为0,0,0即纯黑

if(pixels[i]==-16777216)...{
//转为透明色
//16777215也就是255,255,255即纯白处理为(255<<16)|(255<<8)|
//255后可得此结果
pixels[i]=16777215;
}
}
//在内存中生成图像后映射到image
img=Toolkit.getDefaultToolkit().createImage(
newMemoryImageSource(width,height,pixels,0,width));
}
returnimg;
}


publicvoidrun(DisplayModedisplayMode)...{
_screen=newScreenManager();

try...{
_screen.setFullScreen(displayMode);
//初始化图像加载
loadImages();
//动画循环播放
animationLoop();

}finally...{
_screen.restoreScreen();
}
}


publicvoidanimationLoop()...{
longstartTime=System.currentTimeMillis();
longcurrTime=startTime;

//每次比较工作时间,无效时将退出运作

while(currTime-startTime<TIME)...{
longelapsedTime=System.currentTimeMillis()-currTime;
currTime+=elapsedTime;

//改变动画1
_animation1.update(elapsedTime);
//改变动画2
_animation2.update(elapsedTime);
//绘制窗体
Graphics2Dg=_screen.getGraphics();
draw(g);
g.dispose();

//更新显示内容
_screen.update();


try...{
Thread.sleep(20);

}catch(InterruptedExceptionex)...{
}
}

}


publicvoiddraw(Graphicsg)...{
//绘制背景0,0座标
_graphics.drawImage(_bgImage,0,0,null);

//绘制动画于0,0座标
_graphics.drawImage(_animation2.getImage(),0,0,null);
//绘制动画于400,0座标
_graphics.drawImage(_animation1.getImage(),400,0,null);

//将缓存图绘制于窗体0,0座标之上
g.drawImage(_cacheImage,0,0,null);

}

}
上面是一个动画演示用例,创建了一个背景及两个动画角色,效果图如下:
实际上无论多么复杂的画面效果,也是以这些最基础的环节拼凑而成的。
但是我们也都知道,举凡有利也就有弊,首先这种全屏显示方式无疑增加了系统资源损耗,而且由于分辨率改变造成的图像位置偏移也可能会影响到游戏效果,也增加了潜在错误的产生几率。比如国产的纯java网游《海天英雄传》中,虽然游戏初期只提供了全屏的游戏方式,但后来却突然增加窗口模式,我想或多或少也是与此有关的。
所以我个人建议在java游戏开发中应当订制全屏及视窗两种显示模式,由用户选择使用。当然,我并非专业的游戏开发人员,具体的抉择还是应当实际需要而来。