unity3d 统一:如何旋转儿童武器围绕枢轴点在其本地空间

m4pnthwp  于 2023-02-05  发布在  其他
关注(0)|答案(1)|浏览(194)

在我的2D游戏中,玩家Prefab有子元素,这些子元素是武器(比如剑),所以当玩家移动时,武器会随着他的移动在世界空间中平移,同时保持恒定的局部位置(当然,直到攻击时间)。武器会自动指向附近的敌人,并在他们足够接近时摆动。
我想让武器沿着一个摆动弧旋转,这个摆动弧的旋转点定义在武器向敌人方向的一半范围内。一旦武器开始摆动,弧的局部位置和旋转应该保持不变,不再关心敌人的位置,而世界弧显然会随着玩家平移。武器应该沿着这个弧纯粹相对于玩家。
RotateAround似乎只在世界空间中起作用,因此当武器的世界位置在世界空间中时,试图围绕对象旋转时会导致奇怪的问题(以及我想要的枢轴点的世界位置)将随着玩家平移。此外,我需要它旋转的点需要相对于局部空间,因为当玩家移动时,武器需要保持其局部弧度,同时还随着玩家平移。
我还尝试在武器的transform.localposition上使用Vector3.Slerp,这似乎是一个完美的解决方案,但我似乎无法让弧与我想象的一个好的圆挥杆相匹配。
这次袭击包括三个部分:后摆,前摆和恢复。我最关心的部分是前摆弧,因为其他的可以很容易地通过简单的局部旋转和乐平来实现。

const int BACKSWING = 0;
const int FORESWING = 1;
const int RECOVER = 2;
float[] timeFrac = { .15f, .25f, .6f };
float[] rotations = { 120f, -240f, 120f };
float backSwingDistMultiplier = .5f;

//Swing Attack
public override IEnumerator Attack(float startAngle) {

    var totalAttackTime = GetAttackTime();
    var backSwingDist = backSwingDistMultiplier * Range;
    var startPos = transform.localPosition;
    var slerpCenterDiff = PerpDir(dir).normalized;

    //Interpolation arrays
    float[] swingTimes = { timeFrac[BACKSWING] * totalAttackTime,
                           timeFrac[FORESWING] * totalAttackTime,
                           timeFrac[RECOVER] * totalAttackTime };

    float[] startAngles = { startAngle,
                            startAngle + rotations[BACKSWING],
                            startAngle + rotations[BACKSWING] + rotations[FORESWING] };

    Vector3[] swingPositions = { startPos - (dir - slerpCenterDiff ) * backSwingDist,
                                 startPos + dir * Range + slerpCenterDiff * backSwingDist };

    Vector3[] slerpCenters = { (startPos + swingPositions[BACKSWING]) * .5f + slerpCenterDiff ,
                            ((swingPositions[BACKSWING] + swingPositions[FORESWING]) * .5f) + slerpCenterDiff };

    Vector3[] slerpStarts = { startPos - slerpCenters[BACKSWING],
                              swingPositions[BACKSWING] - slerpCenters[FORESWING]};

    Vector3[] slerpEnds = { swingPositions[BACKSWING] - slerpCenters[BACKSWING],
                            swingPositions[FORESWING] - slerpCenters[FORESWING]};
    
    timer = 0;
    float percentDone;
    //A swing attack has backswing, foreswing, and recovery
    for (int swing = 0; swing <= 2; swing++) {
        while (timer < swingTimes[swing]) {
            percentDone = timer / swingTimes[swing];

            //Backswing and Foreswing will slerp
            if (swing < RECOVER) {
                transform.localPosition = Vector3.Slerp(slerpStarts[swing], slerpEnds[swing], percentDone);
                transform.localPosition += slerpCenters[swing];
            } else { //Recover will lerp
                transform.localPosition = Vector3.Lerp(swingPositions[FORESWING], startPos, percentDone);
            }
            transform.localRotation = Quaternion.Euler(0, 0, 
                startAngles[swing] + rotations[swing] * percentDone);
            timer += Time.deltaTime;
            yield return null;
        }
        transform.localRotation = Quaternion.Euler(0, 0, startAngles[swing] + rotations[swing]);

        timer -= swingTimes[swing];
    }
    transform.localPosition = startPos;
}

我只想做一个动画出来,但我需要的范围是动态的,这是几乎不可能实现的关键帧。

brccelvz

brccelvz1#

我能够通过首先在localPosition中定义弧中心来获得我想要的结果:

var backswingArcCenter = (startPos + swingPositions[BACKSWING]) * 0.5f;
var foreswingArcCenter = (swingPositions[BACKSWING] + swingPositions[FORESWING]) * 0.5f;

然后使用添加到玩家世界空间的位置调用RotateAround

if (swing == FORESWING) {
    transform.RotateAround(player.transform.position + foreswingArcCenter, 
           Vector3.forward, Time.deltaTime / swingTimes[swing] * 270f);
} else if (swing == BACKSWING) {
    transform.RotateAround(player.transform.position + backswingArcCenter,
           Vector3.forward, Time.deltaTime / swingTimes[swing] * -180f);
}

相关问题