在任何方向连续滚动一个大的无缝图像

时间:2011-09-09 03:23:21

标签: java android image math

问题与游戏有关,但我认为它可以应用于其他用例。

我的无缝图像大于屏幕。大约两倍大。目标是使该图像可以在表面上的任何方向上滚动。这适用于Android。

我已经看到了一些关于图像的问题/答案,但不是无缝图像。

这里的区别,我的问题是如何在某些点处理滚动,显示的图像可能有4个单独的部分(当用户在对角线的图像中间滚动时)。

当您想要将图像边缘滚动到边缘时,如何做到这一点非常明显。使用世界值,并使用矩形显示您所在位置的“视口”。

类似的东西:

Rect oRect1 = new Rect(dWorldX, dWorldY, dWorldX + canvas.getWidth(), dWorldY + canvas.getHeight());
Rect oRect2 = new Rect(0, 0, canvas.getWidth(), canvas.getHeight());

canvas.drawBitmap(largeBitmapTexture, oRect1, oRect2, new Paint());

但是当位图无缝且连续滚动时,这对我来说是一个编程挑战。我是否使用了一堆if语句?做这样的事情有更有效的方法吗?

编辑:还有一些想法。这里的一个复杂因素是世界(x,y)无限大。因此,我必须使用屏幕宽度和高度的分区来显示需要显示的内容。我原本考虑只进行4次Rect迭代,因为屏幕上的图像永远不会超过4个。如果那有意义的话。我想我对如何计算只是有点模糊。

编辑:这是我现在拥有的代码,以及来自@glowcoder的建议。

public void draw(Canvas canvas){
    int iImagesOverX=0;
    int iImagesOverY=0;

    iImagesOverX = (int)dX / mCloud.getIntrinsicWidth();
    iImagesOverY = (int)dY / mCloud.getIntrinsicHeight();
    mCloud.setBounds(iImagesOverX * (int)mCloud.getIntrinsicWidth() , iImagesOverY * (int)mCloud.getIntrinsicHeight() , (iImagesOverX * (int)mCloud.getIntrinsicWidth()) + mCloud.getIntrinsicWidth() ,  (iImagesOverY * (int)mCloud.getIntrinsicHeight()) + mCloud.getIntrinsicHeight() );
    Log.d(TAG, "bounds:"+ mCloud.getBounds());
    mCloud.draw(canvas);
    mCloud.setBounds((iImagesOverX + 1) * (int)mCloud.getIntrinsicWidth() , iImagesOverY * (int)mCloud.getIntrinsicHeight() , ((iImagesOverX + 1) * (int)mCloud.getIntrinsicWidth()) + mCloud.getIntrinsicWidth() ,  (iImagesOverY * (int)mCloud.getIntrinsicHeight()) + mCloud.getIntrinsicHeight() );
    mCloud.draw(canvas);
    mCloud.setBounds((iImagesOverX + 1) * (int)mCloud.getIntrinsicWidth() , (iImagesOverY + 1)* (int)mCloud.getIntrinsicHeight() , ((iImagesOverX + 1) * (int)mCloud.getIntrinsicWidth()) + mCloud.getIntrinsicWidth() ,  ((iImagesOverY + 1) * (int)mCloud.getIntrinsicHeight()) + mCloud.getIntrinsicHeight() );
    mCloud.draw(canvas);
    mCloud.setBounds((iImagesOverX) * (int)mCloud.getIntrinsicWidth() , (iImagesOverY + 1)* (int)mCloud.getIntrinsicHeight() , ((iImagesOverX) * (int)mCloud.getIntrinsicWidth()) + mCloud.getIntrinsicWidth() ,  ((iImagesOverY + 1) * (int)mCloud.getIntrinsicHeight()) + mCloud.getIntrinsicHeight() );
    mCloud.draw(canvas);
    Log.d(TAG, "imagesoverx:"+ iImagesOverX);
}

我必须将dX和dY添加到边界才能移动图像。但是,一旦你向右移动一个“瓷砖”,第三个瓷砖就不会出现。

所以这部分工作,但不是我需要的地方。有4个面板,但仅仅是因为边界和绘制的4个实例。我需要这4个面板来绘制他们需要的位置,无论我们在x和y的哪个位置。

3 个答案:

答案 0 :(得分:4)

基本思想是你有一个无限的平面,它包含你在各个方向反复重复的图像。然后你有一个“视口” - 你可以将其视为一个限制你对无限平面视图的窗口。换句话说,在视口“下”的无限平面区域是屏幕上实际显示的区域。

因此,当视口的左上角位于(0,0)时,您会看到从(0,0)到(50,50)的无限平面部分(假设您的视口宽度和高度为50像素) )。同样,如果您的视口位于(238,753),那么您将看到从(238,753)到(288,803)的无限平面的一部分。

如果你的图像是100x100,那么你的无限平面看起来像是:

     (-100,-100)  (0,-100)      (100,-100)    (200,-100)
    ******************************************
    *            *             *             *
    *            *             *             *
    *            *             *             *
    *            *             *             *
    *(-100,0)    *(0,0)        *(100,0)      *(200,0)
    ******************************************
    *            *             *             *
    *            *             *             *
    *            *             *             *
    *            *             *             *
    *(-100,100)  *(0,100)      *(100,100)    *(200,100)
    ******************************************
    *            *             *             *
    *            *             *             *
    *            *             *             *
    *            *             *             *
    *(-100,200)  *(0,200)      *(100,200)    *(200,200)
    ******************************************

现在,让我们说视口的左上角是75,75。从图形上看,它看起来像是:

     (-100,-100)  (0,-100)      (100,-100)    (200,-100)
    ******************************************
    *            *             *             *
    *            *             *             *
    *            *             *             *
    *            *             *             *
    *(-100,0)    *(0,0)        *(100,0)      *(200,0)
    ******************************************
    *            *             *             *
    *            *             *             *
    *            *     (75,75) * (125,75)    *
    *            *          #######          *
    *(-100,100)  *(0,100)   #  *  #          *(200,100)
    ************************#*****#***********
    *            *          #  *  #          *
    *            *          #######          *
    *            *    (75,125) * (125,125)   *
    *            *             *             *
    *(-100,200)  *(0,200)      *(100,200)    *(200,200)
    ******************************************              

在这种情况下,您的视口在(75,75),(75,125),(125,75)和(125,125)处有角。因此,您会看到图像的右下角位于(0,0),图像的左下角位于(100,0),依此类推。

现在,假设您实现了一个函数来计算特定点所在的网格。具体来说,它返回包含该点的网格的左上角。几个例子:

gridContainingPoint(0,0)      -> (0,0)
gridContainingPoint(50,50)    -> (0,0)
gridContainingPoint(100,100)  -> (100,100)
gridContainingPoint(123, -27) -> (100,-100)

这个功能的实现非常简单:

Point gridContainingPoint(Point pt) {
    int newX = ((int)Math.floor(pt.x/100f)) * 100;
    int newY = ((int)Math.floor(pt.y/100f)) * 100;
    return new Point(newX, newY);
}

现在,要确定需要绘制哪些图像,请为视口的每个角调用gridContainingPoint()方法。在这个例子中,你会得到:

    gridContainingPoint(75,75)   -> (0,0)
    gridContainingPoint(75,125)  -> (0,100)
    gridContainingPoint(125,75)  -> (100, 0)
    gridContainingPoint(125,125) -> (100, 100)

现在,您明确知道需要绘制哪些图像才能完全覆盖视口。

在绘制图像之前,必须正确设置视口。默认情况下,在画布上开始绘制时,视口位于(0,0)。因此,您只能看到在(0,0)x(50,50)区域中绘制到画布上的内容。但是,我们希望视口位于(75,75),以便我们在区域(75,75)x(125,125)中看到事物。为此,您可以调用Canvas.translate()方法。例如,canvas.translate(-75,-75)。它必须是负数,因为它在概念上将视图移动到视口下方,而不是移动视口。

现在,使用来自gridContainingPoint()的4次调用的信息,您可以在(0,0),(0,100),(100,0)和(100,100)处绘制图像。你完成了:))

要记住的一些事项 - 此示例中使用的数字不是您要使用的实际数字。

首先,视口的大小不会是50x50,但它将是绘制时视图的实际大小。您可以使用View.getWidth()View.getHeight()来获取此信息。

其次,您需要根据图像大小调整网格大小和相关计算 - 我怀疑您使用的是100x100的图像。

最后,请记住,当您为视口的每个角调用gridContainingPoint()方法时,它可能会为多个角返回相同的网格。例如,如果您的视口位于(25,25),那么它将为每个角返回(0,0)图像,因为(25,25),(25,75),(75,25)和( 75,75)都在图像内,从(0,0)到(100,100)。因此,这将是您必须绘制的唯一图像,以便完全覆盖视口。

答案 1 :(得分:0)

我认为谷歌地图在您平移时会做类似的事情。 他们将MAP分成许多较小的瓷砖(128x128或196x196)。 如果你想要更顺畅的滚动,你需要提前预加载一些BITMAP。 这些都是位图虚拟化技术。

答案 2 :(得分:0)

由于您知道自己的图像比画布大得多,因此您知道一次只能在屏幕上显示图像的一个角落。这很有用。

让你的当前位置为x,y,原点为0,0。让你的图像的尺寸为w,h

计算您的原始图像数量。这只是整数除法中的x / w和y / h。设想的结果是水平的i图像和垂直的j图像。

始终以(i * w,j * h)((i + 1)* w,j * h)(i * w,(j + 1)* h)和((i + 1))绘制图像*瓦特,第(j + 1)*小时)。在画布上绘制4幅图像不会成为一个重大的表现。其中大部分将最终离开屏幕而被忽略。然后您可以继续正常绘制屏幕。这将保证您始终拥有完整的背景,无论" seam"谎言(是的,我知道它是无缝的,但这意味着我们无法看到边界。当然,它仍然存在。)