java学习第十周总结 --- Graphics
下周就开始做游戏项目了。这周主要是对做游戏中需要用到的知识和要点进行学习。虽然游戏项目还未正式启动,但是之前的准备还是要做的。将从四个部分来对所要使用到的知识点进行总结。
首先从绘图类Graphics开始进行总结。在java的基础类库中,提供了专门绘图的类Graphics和他的子类Graphics2D。这两个类都是抽象类,因此不允许实例化。一般都是通过容器的paint方法来调用。至于如何调用我们不需要关心,我们只需要在paint方法中使用该画笔绘图就行了。
Graphics和Graphics2D
一般绘图我们使用Graphics这个类来进行,但是有时候他并不能满足我们的需要。由于这个类所绘制出的图形不能消除锯齿,比如象画圆、曲线等等,或者需要对绘图进行一些高级处理,这个时候我们就应该使用Graphics2D,这个类除类拥有Graphics的所有方法之外,还扩展了一些其他方法。具体请参看javaAPI.
public void paint(Graphics g){
g.drawdrawOval(x, y, width, height)
.
.
.
}
public void paint(Graphics g){
Graphics2D g = (Graphics2D)g
g.drawdrawOval(x, y, width, height)
.
.
.
}
上面第一种就是使用Graphics进行绘图,第二种是使用Graphics2D进行绘图。为了演示Graphics2D的一些特性,下面我们在窗口上绘制一个图片,然后让这个图片逐渐变亮和变暗,类似于一些游戏的开头广告。在这个小程序中我们将使用图像跟踪器MediaTracker和双缓冲,这两点将在后面的总结中介绍。
任意找张图片作为背景图,图片格式只能是jpg、gif、png。然后放在类路径的根目录下,我这里图片的路径是/img/start.jpg。
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Toolkit;
import javax.swing.JFrame;
public class G2DExample extends JFrame implements Runnable {
private Image img;
private int imgWidth;
private int imgHeight;
private float filter = 1 ;
G2DExample() {
img = Toolkit.getDefaultToolkit().getImage( " img/start1.jpg " );
MediaTracker tracker = new MediaTracker( this );
tracker.addImage(img, 0 );
try
{
tracker.waitForID( 0 );
}
catch (InterruptedException e)
{
e.printStackTrace();
}
imgWidth = img.getWidth( this );
imgHeight = img.getHeight( this );
setSize(imgWidth, imgHeight);
setVisible( true );
Thread thread = new Thread( this );
thread.start();
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Image bufferImg = this .createImage(imgWidth, imgHeight);
Graphics2D bufferGraphics = (Graphics2D) bufferImg.getGraphics();
bufferGraphics.drawImage(img, 0 , 0 , this );
bufferGraphics.setPaint(Color.BLACK);
bufferGraphics.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, filter));
bufferGraphics.fillRect( 0 , 0 , imgWidth, imgHeight);
g2.drawImage(bufferImg, 0 , 0 , this );
}
public static void main(String [] args) {
new G2DExample();
}
public void run() {
while ( true )
{
for ( int i = 10 ; i >= 0 ; i -- )
{
filter = ( float ) i / 10 ;
try
{
Thread.sleep( 50 );
}
catch (InterruptedException e)
{
e.printStackTrace();
}
repaint();
}
for ( int i = 0 ; i <= 10 ; i ++ )
{
filter = ( float ) i / 10 ;
try
{
Thread.sleep( 50 );
}
catch (InterruptedException e)
{
e.printStackTrace();
}
repaint();
}
}
}
}
上面这个程序使用了双缓冲技术,这个将放在后面介绍。使图片渐变的关键代码是这三句:
bufferGraphics.setPaint(Color.BLACK);
bufferGraphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, filter));
bufferGraphics.fillRect(0, 0, imgWidth, imgHeight);
bufferGraphics就是使用的是Graphics2D类,setPaint是设置矩形的颜色,setComposite就是用来设置颜色的alpha值,通过线程不断的改变这个值,就可以达到颜色透明度的渐变。setComposite的参数是Composite,而AlphaComposite是Composite的子类,不过这个类不能通过new来创建,而需要他的一个静态方法getInstance来创建,这个方法有两个参数,第一个参数需要定义合成规则,具体的一些规则参看AlphaComposite介绍。第二个参数指定alpha值,最后使用fillRect填充矩形,这样通过不停的改变第二个参数的值就可以达到图像透明度的渐变效果。
上面这个程序是直接在JFrame上paint方法绘制图形的,因为JFrame并不具备双缓冲的功能,因此需要我们显示的使用双缓冲来作图,否则就会出现闪烁的情况。其实在Swing组件中,有许多轻量型组件默认就具备双缓冲功能,而不需要我们自己去实现该功能。只要是继承自JComponent的组件都具备双缓冲的功能。因此这里我们可以使用JPanel容器,在这上面绘制图形,效果和上面的程序一样。
img = Toolkit.getDefaultToolkit().getImage( " img/start1.jpg " );
MediaTracker tracker = new MediaTracker( this );
tracker.addImage(img, 0 );
try
{
tracker.waitForID( 0 );
}
catch (InterruptedException e)
{
e.printStackTrace();
}
imgWidth = img.getWidth( this );
imgHeight = img.getHeight( this );
/*
* 这里创建一个继承自JPanel类的匿名内部类,覆盖paintComponent方法进行绘图
* 引用变量panel指向该对象.
*/
panel = new JPanel() {
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(img, 0 , 0 , this );
g2.setPaint(Color.BLACK);
g2.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, filter));
g2.fillRect( 0 , 0 , imgWidth, imgHeight);
}
};
/* 将panel加在JFrame的容器面板里面 */
this .getContentPane().add(panel);
setSize(imgWidth, imgHeight);
setVisible( true );
Thread thread = new Thread( this );
thread.start();
}
public static void main(String [] args) {
new G2DExampleJPanel();
}
public void run() {
while ( true )
{
for ( int i = 10 ; i >= 0 ; i -- )
{
filter = ( float ) i / 10 ;
try
{
Thread.sleep( 50 );
}
catch (InterruptedException e)
{
e.printStackTrace();
}
repaint();
}
for ( int i = 0 ; i <= 10 ; i ++ )
{
filter = ( float ) i / 10 ;
try
{
Thread.sleep( 50 );
}
catch (InterruptedException e)
{
e.printStackTrace();
}
repaint();
}
}
}
}
从效果上看在这两个程序没什么区别,后一个程序是在JFrame的容器面板里面进行图形的绘制。这里需要注意的是如果在JPanel或者其他继承自JComponent的容器里绘图时,需要覆盖paintComponent方法,而不是覆盖paint方法。虽然可以这么做,但是这并不是最好的方式。
在下一篇里将总结游戏中如何绘制图片,以及解决闪烁,拖屏等问题的方案。