我正在使用C#脚本在Unity中开发一个等距2D游戏。角色将能够以8种不同的方向运行。
我试图根据鼠标位置触发正在运行的动画。我的脚本运行正常,但我认为不是解决此问题的最佳方法。
首先,我有一个可能的方向枚举:
public enum Orientations {N,NE,E,SE,S,SW,W,NW,NONE}
我写了一个方法,它返回一个基于运动的Orientations值。这是因为我想基于运动触发动画,因此角色将始终关注运动的方向:
public static Orientations GetOrientation(Vector2 movement)
{
if (movement.x == 0 && movement.y == 1)
{
return Orientations.N;
}
else if (movement.x == 1 && movement.y == 0)
{
return Orientations.E;
}
else if (movement.x == 0 && movement.y == -1)
{
return Orientations.S;
}
else if (movement.x == -1 && movement.y == 0)
{
return Orientations.W;
}
else if (movement.x == -1 && movement.y == 1)
{
return Orientations.NW;
}
else if (movement.x == 1 && movement.y == 1)
{
return Orientations.NE;
}
else if (movement.x == -1 && movement.y == -1)
{
return Orientations.SW;
}
else if (movement.x == 1 && movement.y == -1)
{
return Orientations.SE;
}
return Orientations.NONE;
}
接下来,我在角色和屏幕之间获得鼠标角度。
public static float GetMousePosition(Transform transform)
{
float cameraDistance = Camera.main.transform.position.y - transform.position.y;
Vector3 mousePosition = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, cameraDistance));
float angleRadius = Mathf.Atan2(mousePosition.y - transform.position.y, mousePosition.x - transform.position.x);
float angle = (180 / Mathf.PI) * angleRadius;
angle = (angle < 0) ? angle + 360 : angle;
return angle;
}
然后,我在Vector2中转换角度,因此我可以通过角色移动和鼠标位置在触发动画之间切换:
public static Vector2 AngleToVectorDirection(Transform transform)
{
Vector2 direction = new Vector2(0,0);
float angle = GetMousePosition(transform);
if(angle >= 67.5 && angle < 112.5)
{
direction = new Vector2(0,1);
}
else if (angle >= 112.5 && angle < 157.5)
{
direction = new Vector2(-1,1);
}
else if (angle >= 157.5 && angle < 202.5)
{
direction = new Vector2(-1, 0);
}
else if (angle >= 202.5 && angle < 247.5)
{
direction = new Vector2(-1, -1);
}
else if (angle >= 247.5 && angle < 292.5)
{
direction = new Vector2(0, -1);
}
else if (angle >= 292.5 && angle < 337.5)
{
direction = new Vector2(1, -1);
}
else if (angle >= 337.5 || angle < 22.5)
{
direction = new Vector2(1, 0);
}
else if (angle >= 22.5 && angle < 67.5)
{
direction = new Vector2(1, 1);
}
return direction;
}
为了完成,我按照我提到的方式返回方向:
public static Orientations GetOrientationByMovement(Transform transform, Vector2 movement)
{
Vector2 orientation;
if (!Input.GetButton("Fire1"))
{
orientation = movement;
}
else
{
orientation = AngleToVectorDirection(transform);
}
return GetOrientation(orientation);
}
此Orientation由触发动画的AnimationController脚本接收。
我不能简单地旋转角色,或翻转精灵,或类似的东西,因为它是基于动画的。
你在这里做两次工作。因此,从优化的角度来看,这不是理想的,但在设计的角度来看,可以很好地分离作品。问题是运动参数在GetOrientationByMovement方法中来自何处?你可以使用方向枚举吗?如果是,那么这大大简化了你的代码!你最终得到:
public static Orientations AngleToVectorDirection(Transform transform)
{
float angle = GetMousePosition(transform);
if(angle >= 67.5 && angle < 112.5)
{
return Orientations.N;
}
else if (angle >= 112.5 && angle < 157.5)
{
return Orientations.NW;
}
else if (angle >= 157.5 && angle < 202.5)
{
return Orientations.W;
}
else if (angle >= 202.5 && angle < 247.5)
{
return Orientations.SW;
}
else if (angle >= 247.5 && angle < 292.5)
{
return Orientations.S;
}
else if (angle >= 292.5 && angle < 337.5)
{
return Orientations.SE;
}
else if (angle >= 337.5 || angle < 22.5)
{
return Orientations.E;
}
else if (angle >= 22.5 && angle < 67.5)
{
return Orientations.NE;
}
}
还要尽量保持代码的一致性。如果你在if语句中使用返回值,那么对于这两个函数都要这样做,在外面使用它时,无处不在。请注意,在AngleToVectorDirection的实现中,您不需要在开头创建新向量,因为您可以覆盖所有角度。
回答我自己的问题:
通过刚刚发布的Unity的3.8f1补丁,我在其中发现了一个以非常简单的方式触发动画的演示项目。
它们使用IsometricCharacterRenderer脚本,其中使用Animator.Play()作为参数传递string []的值,基于玩家移动。
public static readonly string[] staticDirections = { "Static N", "Static NW", "Static W", "Static SW", "Static S", "Static SE", "Static E", "Static NE" };
public static readonly string[] runDirections = { "Run N", "Run NW", "Run W", "Run SW", "Run S", "Run SE", "Run E", "Run NE" };
public void SetDirection(Vector2 direction)
{
//use the Run states by default
string[] directionArray = null;
//measure the magnitude of the input.
if (direction.magnitude < .01f)
{
//if we are basically standing still, we'll use the Static states
//we won't be able to calculate a direction if the user isn't pressing one, anyway!
directionArray = staticDirections;
}
else
{
//we can calculate which direction we are going in
//use DirectionToIndex to get the index of the slice from the direction vector
//save the answer to lastDirection
directionArray = runDirections;
lastDirection = DirectionToIndex(direction, 8);
}
//tell the animator to play the requested state
animator.Play(directionArray[lastDirection]);
}
为了获得方向指数,他们以一个角度转换运动,就像我做的那样,但是以一种聪明的方式。
public static int DirectionToIndex(Vector2 dir, int sliceCount)
{
//get the normalized direction
Vector2 normDir = dir.normalized;
//calculate how many degrees one slice is
float step = 360f / sliceCount;
//calculate how many degress half a slice is.
//we need this to offset the pie, so that the North (UP) slice is aligned in the center
float halfstep = step / 2;
//get the angle from -180 to 180 of the direction vector relative to the Up vector.
//this will return the angle between dir and North.
float angle = Vector2.SignedAngle(Vector2.up, normDir);
//add the halfslice offset
angle += halfstep;
//if angle is negative, then let's make it positive by adding 360 to wrap it around.
if (angle < 0)
{
angle += 360;
}
//calculate the amount of steps required to reach this angle
float stepCount = angle / step;
//round it, and we have the answer!
return Mathf.FloorToInt(stepCount);
}