如何以相同的距离放置物体

How to Position Objects With Same Distances

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

假设你在水平面上工作,你可以为你的每个士兵定义很多旋转,并在平面中找到将笛卡尔坐标 (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) 中工作,您可以在代码中将 .y 更改为 .z

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

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

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

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