绘制基于瓦片的地图[英] Drawing a tile based map

本文是小编为大家收集整理的关于绘制基于瓦片的地图的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

此脚本绘制控件,英雄,表面和地图:

public void render(Canvas canvas) {
        canvas.drawColor(Color.TRANSPARENT);

        Drawable myImage;

        int tileWidth = 50;
        int tileHeight = 50;

        int rowBaseX = 0;
        int rowBaseY = 0;

        int[][] board = new int[][] {
                {0,0,0,0,0,0,2,0,0,0,},
                {0,0,0,0,0,2,2,0,0,0,},
                {0,0,0,0,0,2,0,0,0,0,},
                {0,0,0,0,0,2,0,0,0,0,},
                {0,0,0,2,2,2,0,0,0,0,},
                {0,0,0,2,0,0,0,0,0,0,},
                {0,0,0,2,0,0,0,0,0,0,},
                {0,0,2,2,0,0,0,0,0,0,},
                {0,0,2,0,0,0,0,0,0,0,},
                {0,0,2,0,0,0,0,0,0,0,}
                };
        int mapWidth = 10;
        int mapHeight = 10;

        for (int row = 0; row < mapHeight; row++)
        {

        for (int col = 0; col < mapWidth; col++)
        {
        Resources res = this.getContext().getResources();

        switch(board[row][col])
        {
        case 0:
        myImage = res.getDrawable(R.drawable.tile1);
        break;
        case 1:
        myImage = res.getDrawable(R.drawable.tile2);
        break;
        default:
        myImage = res.getDrawable(R.drawable.tile3);
        break;
        }

        int curL = rowBaseX + (col * tileWidth);
        int curU = rowBaseY + (row * tileHeight);
        int curR = curL + tileWidth;
        int curD = curU + tileHeight;

        myImage.setBounds(curL,curU,curR,curD);
        myImage.draw(canvas);
        }
        }

        droid.draw(canvas);
        butt.draw(canvas);
        butt1.draw(canvas);
        butt2.draw(canvas);
        butt3.draw(canvas);
        buttz.draw(canvas);
        buttz1.draw(canvas);
        buttz2.draw(canvas);
        buttz3.draw(canvas);
        buttx.draw(canvas);
    }

有一个英雄,当玩家用控件将他移动时,必须重新绘制他,而所有其他可抽签的物品也必须重新绘制.问题在于绘制地图是一个漫长的过程,因此我创建的较大的地图,较慢的英雄移动,因为必须绘制地图的每个瓷砖.有没有办法将所有瓷砖放在另一个方法的一个位图上,并在画布方法中绘制一个位图?

推荐答案

最好的选择是仅绘制屏幕上可见的地图部分.这样,无论整体地图成为该地图的绘制始终是恒定的.由于您在网格系统上

heroGridX = hero.x % mapWidth;
heroGridY = hero.y % mapHeight;

从那里您可以通过使用画布的宽度和高度以及网格单元格的宽度和高度来计算要绘制的播放器周围有多少个单元格:

leftGrid = heroGridX - (canvas.getWidth() / tileWidth) / 2;
topGrid = heroGridY - (canvas.getHeight() / tileHeight) / 2;
rightGrid = heroGridX + (canvas.getWidth() / tileWidth) / 2;
bottomGrid = heroGridY + (canvas.getHeight() / tileHeight) / 2;

您可以使用数据结构独立于英雄来存储这些值,只有一旦玩家靠近边缘来滚动地图就可以移动它们.这样,英雄就在不滚动地图的情况下上下移动,直到将X或Y像素置于瓷砖的边缘为止.而不是在渲染程序中计算这些.

这将使用比将整个地图绘制成一个大的位图要少得多.在更少的CPU时间内,绘制大量的位图正在交易更多的内存使用量.随着地图的增长,绘制地图所需的内存也会变得更大.该算法仅保持绘制地图是一个常数的,因为屏幕的大小在运行时不会变化.而且,与其他选项相比,随着地图的增长,您的内存使用情况不会变得更大(与在画布中绘制更多的瓷砖相比,它的生长很小).一个重要的事实是,如果您确实得到了更大的屏幕(例如平板电脑与手机).该算法也可以正确扩展,以便玩家会看到地图的更多周围地形.

其他推荐答案

如果在应用程序之外创建静态图不是一个选项,则可以将静态内容的图与动态内容分开.

创建Bitmap,用它创建Canvas,然后在该Canvas上绘制地图.您只需要一次.在另一个Canvas上每帧渲染一次动态内容. 然后在SurfaceView的"真实" Canvas上绘制两个Bitmap.看起来像这样:

曾经每映射的零件:

Bitmap mapBitmap = Bitmap.createBitmap(width, height, myConfig);
Canvas c = new Canvas(mapBitmap);
//draw map on c

...和每帧一次的部分:

//draw dynamic stuff    
Canvas canvas = getHolder().lockCanvas();
    canvas.drawBitmap(mapBitmap, null, targetRect, null);
    canvas.drawBitmap(heroBitmap, null, targetRect, null);
    getHolder().unlockCanvasAndPost();

编辑:

您像以前一样绘制瓷砖,但将mapbitmap- Canvas作为参数传递而不是"真实"画布. myConfig必须是 bitmap.config .以RGB_565为内部格式.

本文地址:https://www.itbaoku.cn/post/102651.html

问题描述

This script draws the controls, hero, surface and the map:

public void render(Canvas canvas) {
        canvas.drawColor(Color.TRANSPARENT);

        Drawable myImage;

        int tileWidth = 50;
        int tileHeight = 50;

        int rowBaseX = 0;
        int rowBaseY = 0;

        int[][] board = new int[][] {
                {0,0,0,0,0,0,2,0,0,0,},
                {0,0,0,0,0,2,2,0,0,0,},
                {0,0,0,0,0,2,0,0,0,0,},
                {0,0,0,0,0,2,0,0,0,0,},
                {0,0,0,2,2,2,0,0,0,0,},
                {0,0,0,2,0,0,0,0,0,0,},
                {0,0,0,2,0,0,0,0,0,0,},
                {0,0,2,2,0,0,0,0,0,0,},
                {0,0,2,0,0,0,0,0,0,0,},
                {0,0,2,0,0,0,0,0,0,0,}
                };
        int mapWidth = 10;
        int mapHeight = 10;

        for (int row = 0; row < mapHeight; row++)
        {

        for (int col = 0; col < mapWidth; col++)
        {
        Resources res = this.getContext().getResources();

        switch(board[row][col])
        {
        case 0:
        myImage = res.getDrawable(R.drawable.tile1);
        break;
        case 1:
        myImage = res.getDrawable(R.drawable.tile2);
        break;
        default:
        myImage = res.getDrawable(R.drawable.tile3);
        break;
        }

        int curL = rowBaseX + (col * tileWidth);
        int curU = rowBaseY + (row * tileHeight);
        int curR = curL + tileWidth;
        int curD = curU + tileHeight;

        myImage.setBounds(curL,curU,curR,curD);
        myImage.draw(canvas);
        }
        }

        droid.draw(canvas);
        butt.draw(canvas);
        butt1.draw(canvas);
        butt2.draw(canvas);
        butt3.draw(canvas);
        buttz.draw(canvas);
        buttz1.draw(canvas);
        buttz2.draw(canvas);
        buttz3.draw(canvas);
        buttx.draw(canvas);
    }

There is a hero, which has to be redrawn when player moves him with the controls, and all other drawables also has to be redrawn. The problem is that drawing a map is a long process, so the bigger map i create, the slower hero moves, because every tile of the map has to be painted. Is there a way to put all the tiles to a one bitmap in other method and draw that one bitmap in the canvas method?

推荐答案

The best option is to only draw the portion of your map that is visible on the screen. That way no matter how big the overall map becomes the drawing of that map is always constant. Since you're on a grid system you can easily figure out which cell the hero is in:

heroGridX = hero.x % mapWidth;
heroGridY = hero.y % mapHeight;

From there you can calculate how many cells around the player you want to draw by using the width and height of the canvas and the constant size of your grid cell's width and height:

leftGrid = heroGridX - (canvas.getWidth() / tileWidth) / 2;
topGrid = heroGridY - (canvas.getHeight() / tileHeight) / 2;
rightGrid = heroGridX + (canvas.getWidth() / tileWidth) / 2;
bottomGrid = heroGridY + (canvas.getHeight() / tileHeight) / 2;

You could use a data structure to store these values independent of the hero and only move them once the player gets close to an edge to scroll the map. That way the hero moves up and down without scrolling the map until they get X or Y pixels to the edge of a tile. Instead of calculating these inside the rendering routine.

This will use far less memory than drawing the entire map into one large bitmap. Drawing into a large bitmap is trading more memory usage for less CPU time. As your map grows larger so does the memory needed to draw that map. This algorithm merely keeps drawing the map a constant because the size of your screen doesn't change at runtime. And, in comparison to the other option your memory usage doesn't grow any larger as your map grows larger (it grows very small in comparison to drawing more tiles in a canvas). One important fact about this is that if you did get a larger screen (say a tablet vs a phone). This algorithm will scale up properly too so the player will see more of the surrounding terrain of the map.

其他推荐答案

If creating a static map outside your application is not an option, you could separate the drawing of static content from the dynamic content.

Create a Bitmap, create a Canvas with it and draw your map on that Canvas. You only need to do this once. Render the dynamic content once per frame on another Canvas. Then draw both Bitmaps on the "real" Canvas of your SurfaceView. It could look like this :

the once-per-map part :

Bitmap mapBitmap = Bitmap.createBitmap(width, height, myConfig);
Canvas c = new Canvas(mapBitmap);
//draw map on c

...and the once-per-frame part:

//draw dynamic stuff    
Canvas canvas = getHolder().lockCanvas();
    canvas.drawBitmap(mapBitmap, null, targetRect, null);
    canvas.drawBitmap(heroBitmap, null, targetRect, null);
    getHolder().unlockCanvasAndPost();

EDIT:

You draw your tiles like you used to with myImage.draw(canvas), but passing the mapBitmap-Canvas as argument instead of your "real" canvas. myConfig has to be a Bitmap.Config. Take the RGB_565 as it is the internal format.