【问题标题】:How to Position Objects With Same Distances如何定位具有相同距离的对象
【发布时间】:2021-09-16 23:59:00
【问题描述】:

我正在 Unity 中编写一个游戏,你的士兵数量会因某些触发器而增加/减少。我想将我的士兵对象定位为一个完整的圆圈,因此即使它们的数量增加或减少,它们也将始终彼此靠近(如相同的距离)。我该如何管理?

【问题讨论】:

  • 目前1个士兵在什么位置?您只需通过 for 循环运行该函数,并将每个士兵的向量位置增加 x 和 y

标签: visual-studio unity3d game-physics


【解决方案1】:

您可以从一些简单的相对有序的位置分布开始,然后通过应用动态系统方法/梯度体面类型迭代,您可以让位置收敛到更结构化的模式。我用python写了这样的实现,它是向量化的形式,但我还添加了一个带有for循环的等效函数,以说明函数的结构。最终的有序模式的灵感来自于稳定平衡位置,如果它们被弹簧固定,一堆相同半径 r 的圆盘会形成,每两个圆盘一个。为了简化计算,我将弹簧张力平方,从而避免平方根,因此与典型的物理模型不完全一样,但接近它。

import numpy as np
import matplotlib.pyplot as plt

def Grad(pos, r):
    Dq = - pos[:, :, np.newaxis] + pos[:, np.newaxis, :] 
    D = Dq[0,:,:]*Dq[0,:,:] + Dq[1,:,:]*Dq[1,:,:] + np.identity(Dq.shape[1]) 
    Dq = (1 - r**2 / D) * Dq
    return - Dq.sum(axis=2)

def Grad_flow(q_, r, step):
    Q = q_
    n_iter = 0
    while True:
        n_iter = n_iter + 1 # you can count the number of iterations needed to reach the equilibrium
        Q_prev = Q
        Q = Q - step * Grad(Q, r) 
        if np.sum(np.abs((Q.T).dot(Q) - (Q_prev.T).dot(Q_prev))) < 1e-5:
            return Q

'''
Test:
'''

p = np.array([[-3, 3], [-1, 3], [1,3], [3,3],
              [-3, 1], [-1, 1], [1,1], [3,1],
              [-3,-1], [-1,-1], [1,-1], [3,-1],
              [-3,-3], [-1, -3], [1, -3], [3,-3], 
              [-2, 1], [-1,2],[2,-2], [-2,-2], 
              [2,2], [2,0]]).T
r = 0.5
step = 0.01
q = Grad_flow(p, r, step)

'''
Plot:
'''

fig, axs = plt.subplots(1,1)
axs.set_aspect('equal')

axs.plot(q[0,:], q[1,:], 'ro')
axs.plot(p[0,:], p[1,:], 'bo')

plt.grid()
plt.show()

你从蓝色位置开始,然后让它们收敛到红色位置:

这是 Grad 函数的循环版本:

def Grad(pos, r):
    grad = np.zeros(pos.shape, dtype=float)
    for i in range(pos.shape[1]):
        for j in range(pos.shape[1]):
            if not i==j:
                d_pos_0 = pos[0, i] - pos[0, j]
                d_pos_1 = pos[1, i] - pos[1, j]
                m = d_pos_0*d_pos_0 + d_pos_1*d_pos_1
                m = 1 - r*r / m
                grad[0, i] = grad[0, i]  +  m * d_pos_0
                grad[1, i] = grad[1, i]  +  m * d_pos_1
    return grad

当然,所有这些都有点启发式,我不能保证完全通用,所以你必须玩并选择参数r,这是位置之间的一半距离,迭代步长step,初始位置p等等。

【讨论】:

  • 谢谢,这就是我要找的。​​span>
【解决方案2】:

假设您在水平平面上工作,您可以为每个士兵定义多少旋转,并在平面中找到将笛卡尔坐标 (x, y) 转换为极坐标 (R, fi) 的点,添加theta 到 fi 然后再转换回笛卡尔:

// Rotate B around A by angle theta
private (float x, float y) Rotate(
  (float x, float y) A, 
  (float x, float y) B, 
  float theta) {
  
  float fi = Math.Atan2(B.y - A.y, B.x - A.x) + theta;
  float R = Math.Sqrt((A.y - B.y) * (A.y - B.y) + (A.x - B.x) * (A.x - B.x));

  return (A.x + R * Math.Cos(fi), A.y + R * Math.Sin(fi));
}

另一个做同样事情但不使用极坐标的选项:

// Rotate B around A by angle theta clockwise
private (float x, float y) Rotate(
      (float x, float y) A,
      (float x, float y) B,
      float theta)
{
        float s = Math.Sin(theta);
        float c = Math.Cos(theta);

        // translate point back to origin:
        B.x -= A.x;
        B.y -= A.y;

        // rotate point clockwise
        float xnew = B.x * c - B.y * s;
        float ynew = B.x * s + B.y * c;

        // translate point back:
        B.x = xnew + A.x;
        B.y = ynew + A.y;

        return B;
}

如果你想让你的士兵均匀分布在一个圆圈里,你需要用float angle = 360 / numSoldiers;计算每个人的旋转角度。

如果您的游戏是 3d 并且您在平面 (XZ) 中工作,您可以在代码中将 .ys 更改为 .zs。

您还可以检查算法在简单的统一项目立方体或控制台 c# 应用程序中的工作方式,以了解它们并检查它们如何执行向量端点围绕其原点的旋转以返回旋转点。我认为这就是你需要为你的士兵的位置找到兴趣点。

【讨论】:

  • 感谢您的回复,但我想我被误解了。例如,我想像 Count Masters 游戏一样定位我的士兵。我想把它们累积起来。
猜你喜欢
  • 2020-03-03
  • 1970-01-01
  • 1970-01-01
  • 2015-07-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多