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

标签 java android image math

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

我有一个比屏幕大的无缝图像。大约两倍大。目标是使此图像可在表面上的任何方向滚动。这是针对 Android 的。

我见过一些关于一般图像的问题/答案,但不是无缝图像。

这里的区别和我的问题是在某些时候如何处理滚动,可以显示 4 个独立的图像 fragment (当用户沿对角线滚动到图像的一半时)。

当您想要将图像边滚动到边时,该怎么做是很明显的。使用世界值,并使用矩形显示您所在位置的某种“视口(viewport)”。

类似于:

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) 无限大。因此,我必须使用屏幕宽度和高度的划分来显示需要显示的内容。我最初考虑只对 Rect 进行 4 次迭代,因为屏幕上的图像永远不会超过 4 block 。如果那有意义的话。我想我只是对如何计算有点模糊。

编辑:这是我现在的代码,来自@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 添加到边界才能使图像移动。但是,一旦您向右移动一个“方 block ”,第三个方 block 就不会出现。

所以这部分有效,但不是我需要的地方。有 4 个面板,但只是因为边界和绘制的 4 个实例。无论我们使用 x 和 y 在哪里,我都需要这 4 个面板来绘制它们需要的位置。

最佳答案

基本思想是您有一个无限平面,其中包含您在各个方向上一遍又一遍地重复的图像。然后你有一个“视口(viewport)”——你可以把它想象成一个限制你对无限平面的看法的窗口。换句话说,视口(viewport)“下方”的无限平面区域就是屏幕上实际显示的区域。

因此,当视口(viewport)的左上角位于 (0,0) 时,您会看到从 (0,0) 到 (50,50) 的无限平面部分(假设视口(viewport)的宽度和高度为 50 像素).同样,如果您的视口(viewport)位于 (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)
    ******************************************

现在,假设视口(viewport)的左上角位于 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)
    ******************************************              

在这种情况下,您的视口(viewport)在 (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);
}

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

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

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

在绘制图像之前,您必须正确设置视口(viewport)。默认情况下,当您开始在 Canvas 上绘图时,视口(viewport)位于 (0,0)。所以你只会看到在 (0,0)x(50,50) 区域绘制到 Canvas 上的东西。但是,我们希望视口(viewport)位于 (75,75),这样我们就可以看到 (75,75)x(125,125) 区域中的东西。为此,您调用 Canvas.translate() 方法。例如,canvas.translate(-75,-75)。它必须是负数,因为它在概念上是在视口(viewport)下方移动 Canvas ,而不是移动视口(viewport)。

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

需要牢记的几件事 - 此示例中使用的数字不是您要使用的实际数字。

一方面,视口(viewport)的大小不会是 50x50,而是绘制时 View 的实际大小。您可以使用 View.getWidth()View.getHeight() 获取此信息。

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

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

关于java - 在任何方向连续滚动一个大的无缝图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7356819/

相关文章:

java - TableLayout 中表格行的索引

android - 从版本控制中 checkout 项目失败

在手机中安装 Android

java - Java Swing 中的 PNG 渲染错误(颜色深度低)

关于绘图和图像( Canvas 、SVG)的 HTML5 建议

java - 如何通过 java 和 c 之间的套接字正确发送数据?

java - Repast 模型启动时出现的问题

Java - public int indexOf(int ch)

c++ - 合并两个显示亮度的图像

java - 什么是NullPointerException,我该如何解决?