游戏引擎理论与实现系列03-生成图像

Tick机制

public class Ticker {
    public void tick() {
    }
}

游戏服务器程序内部都会统一设计所谓的tick机制,这个机制一般来说有两个用途,一是许多业务模块都会有定时处理的需求,如:游戏中一天时间的轮转;二是游戏服务器内部会有一些日常主动驱动的事件,如:水,火山熔浆动画,这些都在tick中处理。关于tick机制的实现和其中一些问题, 我们将来会详细介绍和探讨,对于现在, 我们只需从整体结构上知道tick机制的存在和重要性。

Bitmap(位图)

又称栅格图(Raster graphics),是使用像素阵列(Pixel-array/Dot-matrix點陣)来表示的图像。

根据位深度,可将位图分为1、4、8、16、24及32位图像等。每个像素使用的信息位数越多,可用的颜色就越多,颜色表现就越逼真,相应的数据量越大。例如,位深度为 1 的像素位图只有两个可能的值(黑色和白色),所以又称为二值位图。位深度为 8 的图像有 28(即 256)个可能的值。位深度为 8 的灰度模式图像有 256 个可能的灰色值。

RGB图像由三个颜色通道组成。8 位/像素的 RGB 图像中的每个通道有 256 个可能的值,这意味着该图像有 1600 万个以上可能的颜色值。有时将带有 8 位/通道 (bpc) 的 RGB 图像称作 24 位图像(8 位 x 3 通道 = 24 位数据/像素)。通常使用24位RGB组合数据位表示的的位图称为真彩色位图。

RGB

位图颜色的一种编码方法,用红(Red)、绿(Green)、蓝(Blue)三原色的光学强度来表示一种颜色。这是最常见的位图编码方法,可以直接用于屏幕显示。

Alpha通道

在原有的图片编码方法基础上,增加像素的透明度信息。图形处理中,通常把RGB三种颜色信息称为红通道、绿通道和蓝通道,相应的把透明度称为Alpha通道。多数使用颜色表的位图格式都支持Alpha通道。

色彩深度[编辑]

色彩深度又叫色彩位数,即位图中要用多少个二进制位来表示每个点的颜色,是分辨率的一个重要指标。常用有1位(单色),2位(4色,CGA),4位(16色,VGA),8位(256色),16位(增强色),24位和32位(真彩色)等。色深16位以上的位图还可以根据其中分别表示RGB三原色或CMYK四原色(有的还包括Alpha通道)的位数进一步分类,如16位位图图片还可分为R5G6B5,R5G5B5X1(有1位不携带信息),R5G5B5A1,R4G4B4A4等等。

Bitmap Class

本引擎所有的图形操作都是通过Bitmap与Screen或图形缓存来完成。我们会发现, Bitmap其实是非常简单。我们从介绍Bitmap类的定义开始。每个像素Pixel为一个32bit 的整数, 因此可以支持24位RGB组合数据位和Alpha通道。 所有的Pixel保存在一个数组结构Pixels中。

public class Bitmap {
    protected int width;
    protected int height;
    protected int[] pixels;

    public Bitmap(int width, int height) {
        this.width = width;
        this.height = height;
        this.pixels = new int[width * height];
    }

    public int getWidth() {
        return width;
    }

    public int getHeight() {
        return height;
    }

    public int[] getPixels() {
        return pixels;
    }
}

Screen

屏幕实质上就是一大Bitmap.

public class Screen extends Bitmap {
    public Screen(int width, int height) {
        super(width, height);
    }

    public void render(Ticker ticker, Bitmap bitMap) {
        this.putBitmap(bitMap, 0, 0);
    }
}

Drawing a Bitmap

以上介绍了Bitmap, 接下我们介绍如何将将一个Bitmap 画到另外一个Bitmap,最常见的,比如,将Bitmap画到屏幕上,对应左上角的位置为(offX, offY)
透明色

    public void putBitmap(Bitmap bitmap, int offX, int offY) {
        int w = bitmap.getWidth();
        int h = bitmap.getHeight();
        int[] p = bitmap.getPixels();
        int[] screenPixels = super.pixels;

        int posX = 0, posY = 0;
        for (int y = 0; y < h; y++) {
            posY = offY + y;
            if (posY < 0 || posY >= super.height) {
                continue;
            }

            int screenRowOffset = posY * width;
            int bitmapRowOffset = y * w;

            for (int x = 0; x < w; x++) {
                posX = offX + x;
                if (posX < 0 || posX >= super.width) {
                    continue;
                }

                screenPixels[screenRowOffset + posX] = p[bitmapRowOffset + x];
            }
        }
    }

Scanning a Bitmap

该函数是putBitmap函数的反向函数,用于获取对应bitmap覆盖的屏幕位置。 注意该返回对象时通过传参数实现的。

    public void getBitMap(BitMap bitmap, int offX, int offY) {
        int w = bitmap.getWidth();
        int h = bitmap.getHeight();
        int[] bitMapPixels = bitmap.getPixels();
        int[] screenPixels = super.pixels;

        int posX = 0, posY = 0;
        int pixel;
        for (int y = 0; y < h; y++) {
            posY = offY + y;
            if (posY < 0 || posY >= super.height) {
                continue;
            }

            int screenRowOffset = posY * width;
            int bitmapRowOffset = y * w;

            for (int x = 0; x < w; x++) {
                posX = offX + x;
                if (posX < 0 || posX >= super.width) {
                    continue;
                }
                pixel = screenPixels[screenRowOffset + posX];
                if(pixel!=0){
                    bitMapPixels[bitmapRowOffset + x] = pixel;
                }
            }
        }
    }

Load Image

读取图像文件并保存到Bitmap。

public class ImageLoader {
    public static BitMap floors = load("/tex/floors.png");

    private static BitMap load(String filename) {
        try {
            URL url = ImageLoader.class.getResource(filename);
            BufferedImage img = ImageIO.read(url);
            int w = img.getWidth();
            int h = img.getHeight();
            BitMap result = new BitMap(w, h);
            int[] pixels = result.getPixels();
            img.getRGB(0, 0, w, h, pixels, 0, w);
            return result;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

主程序

缓冲策略

public void createBufferStrategy(int numBuffers)

生成多缓冲(Multi-buffering)策略, 多缓冲(Multi-buffering)的方式可以显著地改善渲染性能。它将始终根据该缓冲区数参数来创建 bufferstrategy 。首先尝试翻页的缓冲区交换(page-flipping) 策略,然后使用加速缓冲区尝试blitting 策略,最后使用不加速的blitting 策略。

        BufferStrategy bs = this.getBufferStrategy();
        if (bs == null) {
            this.createBufferStrategy(3);
            return;// skip one render
        }

BufferImage and Raster DataBuffer

生成和屏幕一样尺寸的图像缓存BufferedImage。 并用screenPixels指向对应缓存数据的的数组, 以方便程序的操作。

    private BufferedImage screenImage;
    private int[] screenPixels;
    this.screenImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
    DataBufferInt buffer = (DataBufferInt) screenImage.getRaster().getDataBuffer();
    this.screenPixels = buffer.getData();

DrawImage

        Bitmap floors = ImageLoader.sprites;
        this.screen.render(ticker,floors);
        for (int i = 0; i < this.screen.getPixels().length; i++) {
            this.screenPixels[i] = this.screen.getPixels()[i];
        }

        Graphics g = bs.getDrawGraphics();
        g.drawImage(this.screenImage, 0, 0, WIDTH * SCALE, HEIGHT * SCALE, null);
        g.dispose();
        bs.show();
  1. 获取Bitmap对象
  2. 将Bitmap对象画到对应的屏幕对象
  3. 通过循环,将Screen Bitmap 像素设置到 屏幕缓存像素数组
  4. 将屏幕缓存像素数组显示。

参考资料

维基百科-位图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值