将对象移动到场景中的指定点?

Posted

技术标签:

【中文标题】将对象移动到场景中的指定点?【英文标题】:Move objects to specified points in a scene? 【发布时间】:2011-01-23 18:16:25 【问题描述】:

我一直在尝试使用 3dsmax 模型拼凑一个国际象棋游戏。至此,我已经能够导入模型,突出显示我有兴趣移动的选定游戏块,并选择我想要移动到的方格。这是当前状态的截图:

http://img26.imageshack.us/img26/9555/chessk.png

黑色圆圈代表我点击的地方,你可以看到棋子去了哪里。我还没有对它应该去哪里做具体的计算。每当我用选定的棋子点击棋盘时,它总是朝同一个方向移动。这是因为刚刚输入了这个虚拟代码开始:

if ( isObjectSelected && isSquareSelected && moveObject )

    glPushMatrix();
    glTranslatef(0.2f, 0.0f, 0.0f); //PLACEHOLDER-DUMMY CODE


glDrawElements( GL_TRIANGLES, pMaterial->triangleCount * 3, GL_UNSIGNED_INT, model.getIndexBuffer() + pMaterial->startIndex );

if ( isObjectSelected && isSquareSelected )
    glPopMatrix();

我正在考虑做的是在模型加载完成后,以某种方式检查棋子占据棋盘上的哪个方格。 Then, when a piece is selected and a "move to" square is selected, find the x,y,z glTranslate3f to move to that center square.

这是最好的方法吗?似乎随着游戏的进行,我需要单独存储每个部分的 glTranslate。而当一块从它的第 2 点到第 3 点时,我应该计算从原始起点到第 3 点的 glTranslate,对吧?

但是你如何判断一个棋子是否占据一个正方形,你又如何判断两个正方形之间的 glTranslate3f(X, Y, Z) 呢?这是我的 .OBJ 文件中的正方形示例

#
# object Square58
#

v  -37.1874 18.6313 80.7864
v  -67.0955 18.6313 91.4436
v  -56.4384 18.6313 121.3513
v  -26.5306 18.6313 110.6938
# 4 vertices

vn 0.0000 1.0000 -0.0000
# 1 vertex normals

vt 0.0000 0.0000 0.0000
# 1 texture coords

我假设我需要找到每个正方形的中心,并说一旦应用程序知道这块在 square1 中,并且您点击了 sqaure4,计算平移并执行。我只是不确定如何计算每个正方形的中心,并从 square1->square4 中找出平移坐标。

或者我将如何确定哪些棋子从一开始就占据哪个方格。我可以在加载过程中对其进行硬编码,但这将帮助我更多地了解是否有一种合理的方式来完成此操作。

每个方块和游戏块都是一个结构体GroupObject,例如:

//A chess piece or square
struct GroupObject

    std::vector<Material *> materials;
    std::string             objectName;
    std::string             groupName;
    int                     index;
    std::vector<Vector3 *>  vertices;
    Vector3                 center;
;

Vector3 看起来像:

#ifndef VECTOR3_H
#define VECTOR3_H

#include <math.h>

class Vector3

public:
Vector3(float X = 0.0f, float Y = 0.0f, float Z = 0.0f)

    x = X;
    y = Y;
    z = Z;


Vector3 operator+=(const Vector3 &vec)

    return (*this = (*this + vec) );


Vector3 operator+(const Vector3 &vec)

    return Vector3(vec.x + x, vec.y + y, vec.z + z);


Vector3 operator-=(const Vector3 &vec)

    return (*this = (*this - vec) );


Vector3 operator-(const Vector3 &vec)

    return Vector3(x - vec.x, y - vec.y, z - vec.z);


Vector3 operator*=(float num)

    return (*this = (*this * num) );


Vector3 operator*(float num)

    return Vector3(x * num, y * num, z * num);


Vector3 operator/=(float num)

    return (*this = (*this / num) );


Vector3 operator/(float num)

    return Vector3(x / num, y / num, z / num);


Vector3 operator-(void)

    //invert direction of vector
    return Vector3(-x, -y, -z);


float Dot(Vector3 &vec)

    return (x * vec.x + y * vec.y + z * vec.z);


Vector3 operator*(const Vector3 &vec)

    //cross product
    return Vector3( y * vec.z - z * vec.y,
                    z * vec.x - x * vec.z,
                    x * vec.y - y * vec.x );


float Length(void)

    //length of vector
    return sqrt( x * x + y * y + z * z);


Vector3 Normalize(void)

    float length = Length();
    x /= length;
    y /= length;
    z /= length;

    return *this;


float Distance(Vector3 &vec)

    //find distance between two separate vectors
    float disX = vec.x - x;
    float disY = vec.y - y;
    float disZ = vec.z - z;

    return sqrt(disX * disX + disY * disY + disZ * disZ);


bool operator==(Vector3 &vec)

    return (vec.x == x && vec.y == y && vec.z == z);


bool operator!=(Vector3 &vec)

    return !(vec == *this);

公开: 浮动 x, y, z; ;

#endif

我打算用它来存储每个正方形的中心,例如:

Vector3 square->center = X, Y, Z

希望这将有助于它确定移动的位置,以及是否有东西占据了那个空间,但我想我应该得到一些帮助,因为我以前没有这样做过。我也不确定我应该如何计算它,或者仍然完全清楚如何确定一个正方形是否有一个游戏棋子在开始时占据它,或者它是空的。

欢迎任何和所有的帮助。谢谢。

更新

现在我在 3ds max 中看到了一个伙伴,在一个方形对象属性上它说尺寸是 x:40.565 y:40.565

然后我选择了两个正方形,一个在另一个前面。我通过将所有顶点相加并除以顶点的总和来计算每个顶点的中心。我得到了这个:

#square 1
x: 111.12125
y: 18.631268
z: 78.286982

#square 2
x: 82.276817
y: 17.615297
z: 88.545845

但从我原来的例子来看,我能接近移动到正确位置的唯一方法是移动 glTranslatef(0.2f, 0.0f, 0.0f);

上面列出的两个中心之间的差异要大得多。我做错了什么,但没有看到。

【问题讨论】:

你的 operator+= 是用 operator+ 定义的。那是倒退。 【参考方案1】:

[有点离题] 当我编写我的大学项目时,我曾经使用简单的数学公式来确定每条线的交叉点来绘制我的“世界”(它与 check-desk 类似:它是无穷无尽的并且没有颜色差异)。当我得到这些点时,我能够简单地确定哪个特定的方形光标指向。老实说,我在最后一个(“生产”)版本中使用了简单的数学来确定这些中心。

Here 和 there 是我的旧屏幕。

那么,有什么问题吗?当您执行glPushMatrix() 时,您的矩阵将重置为其默认值 - 位置变为 (0, 0, 0) 并重置旋转。如果你想把你的对象放在相交点 line, plane,其中线是从相机原点到光标位置的光线(回忆一下 OpenGL 中的相机理论:实际上,屏幕是相机“位置”点前面的平面) 并且飞机是在那条射线前面的东西。 注意:如果您不定义平面 - 您将无法确定交点。

现在,当你得到交点时,只需执行glTranslatef(&lt;intersection_point&gt;) 然后画你的棋子或其他什么。

如果您对如何确定交点的纯代码感兴趣 - 请在 cmets 中通知我。

希望这会有所帮助。

UPD:这是我之前提到的交叉点代码。它使用sf::Vector3f 结构/类 - 它取自SFML(我将它用于我所有的 GL 项目来处理输入事件并忘记“回合窗口创建”)。您可以将其定义为具有 3 个浮点数的简单结构。函数参数是鼠标光标的坐标。调用它会为您提供交点坐标。

sf::Vector3f ScreenToSpace(int x, int y)

    GLint viewport[4];
    GLdouble modelview[16];
    GLdouble projection[16];
    GLfloat winX, winY, winZ;
    GLdouble posX, posY, posZ;

    glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
    glGetDoublev(GL_PROJECTION_MATRIX, projection);
    glGetIntegerv(GL_VIEWPORT, viewport);

    winX = (float) x;
    winY = (float) viewport[3] - (float) y;
    glReadPixels(x, int(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);

    gluUnProject(winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ);

    return sf::Vector3f((float) posX, (float) posY, (float) posZ);

这是关于如何使用它的示例(据我了解您的问题):

sf::Vector3f v = ScreenToSpace(App.GetInput().GetMouseX(), App.GetInput().GetMouseY()); // get global coordinates
v = SpaceToDeskCorrection(v.x, v.y, v.z); // align v to the check-desk cell center

glPushMatrix(); // reset matrix state (incl. position and rotation) and save previous one
  glTranslatef(v.x, v.y, v.z); // set matrix position to the intersection point
  DrawPawn(); // well, you'll understand this line, right? =)
glPopMatrix();

【讨论】:

好的。我认为,您可以通过使用此功能作为辅助功能确定板边框宽度和/或单元格中心来创建位置校正功能。但是,您也可以像我在大学项目中所做的那样绘制桌子,然后使用顶点和索引数组附加边框之类的东西。如果是这样,您将能够扩展您的国际象棋 =) 我喜欢 SFML。它很简单,拥有我需要的所有功能,而且没有什么用处。你应该试试看。真的。 是的,这是我的错。我昨天忘记提这个了。我道歉。 @Hallik,评论你的源代码:你不觉得如果你的 glPushMatrix()、模型 translate/draw 和 glPopMatrix 在一个 if 块内会更漂亮吗?跨度> 这个函数怎么写?如果是这样的话:找出你桌子上每个单元格的中心(或者弄清楚如何用数学方法计算它——这并不难),然后根据交点坐标决定国际象棋人物应该站在哪里。例如:我选择了对应于桌子上 (a, b) 的点 (x0, y0)。 SpaceToDeskCorrection(x0, y0) 应该返回 (a, b) 单元格中心坐标。如果我将光标移动到 (x1, y1) (例如,从 (x0, y0) 向左下方但仍在同一个单元格内)点,它应该返回与给定的 (x0, y0) 相同的坐标。 【参考方案2】:

我最近自己做了一个跳棋游戏,2D,但同样的原则也适用。首先,我建议您保留一个带有棋盘格的数组,正如您在结构中定义的那样,它有一个中心变量。

要知道每个部分的位置,例如,您可以添加一个变量,该变量是指向给定方块的指针,或者只是像“E4”或“A8”(方块代码)这样​​的表示。有了这个,您将自动获得棋子所在位置的 x,y 坐标,一旦检测到要将其移动到哪个方格,您只需将指针替换为新棋子并获取其中心信息,这就是您想要的位置将作品翻译成。这还有一个额外的好处是,当一个棋子从对手手中接管另一个棋子时,或者只是它是否可以移动到某个方格(检查所有棋子,如果它们中的任何一个正在铺设),您可以更轻松地检查它。在你想去的广场上,不要移动)。

要找出任何正方形的中心,假设您在创建棋盘时不知道它(不是吗?),并对所有构成顶点 x 和 y 坐标进行平均(如果棋盘是铺设在 xy 平面上)。例如,Square58 中心 = (v1(x)+v2(x)+v3(x)+v4(x) ; v1(y)+v2(y)+v3(y)+v4(y))。

我希望这已经足够清楚了,如果不是让我知道你没有得到什么,我会尽力解释。

编辑:对不起,我写错了。在 Square58 中心.. 行中,您将最终总和除以 4(以获得平均值).. 即:

Square58 中心 = ((v1(x)+v2(x)+v3(x)+v4(x))/4 ; (v1(y)+v2(y)+v3(y)+v4(y ))/4).

【讨论】:

我将计算每个正方形的中心,如您所示。一旦我这样做了,我的 glTranslate3f 会是 currentSquare(x,y,z) - squareToMoveTo(x,y,z) 吗?感谢您的帮助。 我猜你每个新的游戏状态每件都做 1 个 glTranslation。在这种情况下,您只需要使用新广场的中心进行 glTranslate。您不会跟踪所有以前的职位,只跟踪最新的职位。比如说,在渲染场景之前,每一块都在 0,0,0。然后,您将 glTranslate 应用到具有中心坐标的每个部分(一旦您移动它,这将是新的正方形) 我得到了每个正方形的中心,并尝试了 glTranslate(x, y, 0.0f);到它的新位置,但仍然将对象发送到板外。我将尝试计算 start->center & end->center 之间的距离,看看会发生什么。然后我将尝试下面的建议来使用 SFML

以上是关于将对象移动到场景中的指定点?的主要内容,如果未能解决你的问题,请参考以下文章

第一步是 THREE.js:尝试将搅拌机模型添加到场景中的问题

记录应仅加载到场景中的目标

在 Unity3d 中将游戏对象动态添加到场景中

Sprite Kit - 使用 Switch Case 将随机 Sprite 节点添加到场景中

在 C++ 中将新的 qml 对象添加到场景中

使用 SpriteKit 场景编辑器设计复杂对象