如何在Unity 3D中创建Ofscreen Enemy指标?

时间:2015-08-14 08:37:11

标签: unity3d

我正在使用下面链接中提到的教程创建一个Ofscreen Enemy指标。但是我可以让指示器旋转指向敌人,但指示器不会从屏幕的一端移动到另一端。

http://gamedevelopment.tutsplus.com/tutorials/positioning-on-screen-indicators-to-point-to-off-screen-targets--gamedev-6644

这是理想的结果:

desired-outcome

直到现在我已经设法弄清楚下面请帮忙。

var screenCenter:Vector3 = new Vector3(0.5, 0.5, 0f);
    //Note coordinates are translated
    //Make 00 the  centre of the screen instead of bottom left
    screenpos -= screenCenter;

    //find angle from center of screen instead of bototom left
    var angle:float = Mathf.Atan2(screenpos.y, screenpos.x);
    angle -= 90 * Mathf.Deg2Rad;

    var cos:float = Mathf.Cos(angle);
    var sin:float = -Mathf.Cos(angle);

    screenpos = screenCenter + new Vector3(sin*150, cos*150, 0);

    //y=mx + b format
    var m:float = cos/sin;
    var ScreenBounds:Vector3 = screenCenter;// * 0.9f;

    //Check up and down first
    if(cos > 0){
        screenpos = new Vector3(ScreenBounds.y/m, ScreenBounds.y, 0);
    }else{//down
        screenpos = new Vector3(-ScreenBounds.y/m, -ScreenBounds.y, 0);
    }
    //If out of bound then get point on appropriate side
    if(screenpos.x > ScreenBounds.x){//Out of bound must be on right
        screenpos = new Vector3(ScreenBounds.x, ScreenBounds.y*m, 0);
    }else if(screenpos.x < ScreenBounds.x){//Out of bound must be on left
        screenpos = new Vector3(-ScreenBounds.x, -ScreenBounds.y*m, 0);
    }
    //Remove the co ordinate translation
    screenpos += screenCenter;
    var DistanceIndicatorRectT = DistanceIndicator.GetComponent(RectTransform);
    DistanceIndicatorRectT.localPosition = new Vector3(screenpos.x * scrWidth/2, screenpos.y * scrHeight/2, DistanceIndicatorRectT.localPosition.z * screenpos.z);
    DistanceIndicator.transform.rotation = Quaternion.Euler(0, 0, angle*Mathf.Rad2Deg);

3 个答案:

答案 0 :(得分:3)

我做了一些与你不同的方法,卡洛斯建议但没有使用物理学。

如果&#34; t&#34;是你的目标,这样你可以在屏幕上以像素为单位获得它的位置(如果它在屏幕外它只是去负值或高于那个宽度的值)

Vector3 targetPosOnScreen = Camera.main.WorldToScreenPoint (t.position);

这个函数返回一个bool,无论Vector3(以像素为单位)是否在屏幕上

bool onScreen(Vector2 input){
    return !(input.x > Screen.width || input.x < 0 || input.y > Screen.height || input.y < 0);
}

我们应该做的第一件事就是检查目标是否在屏幕上,如果它没有继续代码。

if (onScreen (targetPosOnScreen)) {
        //Some code to destroy indicator or make it invisible
        return;
    }

然后简单计算屏幕中心与目标之间的角度。

Vector3 center = new Vector3 (Screen.width / 2f, Screen.height / 2f, 0);
float angle = Mathf.Atan2(targetPosOnScreen.y-center.y, targetPosOnScreen.x-center.x) * Mathf.Rad2Deg;

下一部分代码根据我们刚刚计算的角度确定对象与相机的比较位置。

float coef;
if (Screen.width > Screen.height)
    coef = Screen.width / Screen.height;
else
    coef = Screen.height / Screen.width;

float degreeRange = 360f / (coef + 1);

if(angle < 0) angle = angle + 360;
int edgeLine;
if(angle < degreeRange / 4f) edgeLine = 0;
else if (angle < 180 - degreeRange / 4f) edgeLine = 1;
else if (angle < 180 + degreeRange /  4f) edgeLine = 2;
else if (angle < 360 - degreeRange / 4f) edgeLine = 3;
else edgeLine = 0;

http://s23.postimg.org/ytpm82ad7/Untitled_1.png

图像代表什么价值&#34; edgeLine&#34;将基于目标位置(红色代表相机的视图)和黑色线条划分空间。

然后我们将这个代码设置为Transform&#34; t2&#34; (指示器)纠正位置和角度。

t2.position = Camera.main.ScreenToWorldPoint(intersect(edgeLine, center, targetPosOnScreen)+new Vector3(0,0,10));
t2.eulerAngles = new Vector3 (0, 0, angle);

下面我们有功能&#34;相交&#34;哪个代码是:

Vector3 intersect(int edgeLine, Vector3 line2point1, Vector3 line2point2){
    float[] A1 = {-Screen.height, 0, Screen.height, 0};
    float[] B1 = {0, -Screen.width, 0, Screen.width};
    float[] C1 = {-Screen.width * Screen.height,-Screen.width * Screen.height,0, 0};

    float A2 = line2point2.y - line2point1.y;
    float B2 = line2point1.x - line2point2.x;
    float C2 = A2 * line2point1.x + B2 * line2point1.y;

    float det = A1[edgeLine] * B2 - A2 * B1[edgeLine];

    return new Vector3 ((B2 * C1[edgeLine] - B1[edgeLine] * C2) / det, (A1[edgeLine] * C2 - A2 * C1[edgeLine]) / det, 0);
}

我们向这个函数发送我们需要检查交叉点的相机视线(矩形)的索引,并在屏幕中心和目标位置之间构建一条线。

有关此功能的更好说明,请查看此处:https://www.topcoder.com/community/data-science/data-science-tutorials/geometry-concepts-line-intersection-and-its-applications/

我刚刚修改了A1,B1和C1的值,每个值现在都是4的数组,每个值代表一行相机视图(矩形)所需的值。

如果要实现边距,只需更改指示器的轴心(将实际的精灵渲染器设置为子节点,然后根据需要将其移动到局部空间中)。

接下来的事情就是让这个目标阵列工作,并将所有这些目标放在给定的数组中。希望这会对我有所帮助,对我来说不会太难,这是我第一次在这里发帖:)

答案 1 :(得分:2)

创建一个矩形框对撞机,用于界定屏幕的边框,并在敌人的方向上使用Physics2D.Raycast

碰撞点会告诉您需要绘制绿色箭头的位置。

答案 2 :(得分:0)

在上面的示例中,直角矩形的可见角度定义存在错误。

private void SetIndicatorPosition(Indicator obj)
{

    var target = obj.Target;
    var indicator = obj.PointToTarget;
    if (target == null)
    {

        indicator.SetActive(false);
        return;
    }
    Vector3 targetPosOnScreen = cam.WorldToScreenPoint(target.transform.position);
    if (onScreen(targetPosOnScreen))
    {
        indicator.SetActive(false);
        return;
    }
    indicator.SetActive(true);
    Vector3 center = new Vector3(Screen.width / 2f, Screen.height / 2f, 0);
    float angle = Mathf.Atan2(targetPosOnScreen.y - center.y, targetPosOnScreen.x - center.x) * Mathf.Rad2Deg;
    float scale;
    if (Screen.width > Screen.height)
        scale = Screen.width / Screen.height;
    else
        scale = Screen.height / Screen.width;

    float degreeRange = 360f / (scale + 1);
    float angle2 = Mathf.Atan2(Screen.height - center.y, Screen.width - center.x) * Mathf.Rad2Deg;

    if (angle < 0) angle = angle + 360;
    int edgeLine;
    if (angle < angle2) edgeLine = 0;
    else if (angle < 180 - angle2) edgeLine = 1;
    else if (angle < 180 + angle2) edgeLine = 2;
    else if (angle < 360 - angle2) edgeLine = 3;
    else edgeLine = 0;

    indicator.transform.position = Camera.main.ScreenToWorldPoint(Intersect(edgeLine, center, targetPosOnScreen));
    indicator.transform.eulerAngles = new Vector3(0, 0, angle);
}

Vector3 Intersect(int edgeLine, Vector3 line2point1, Vector3 line2point2)
{
    float[] A1 = { -Screen.height, 0, Screen.height, 0 };
    float[] B1 = { 0, -Screen.width, 0, Screen.width };
    float[] C1 = { -Screen.width * Screen.height, -Screen.width * Screen.height, 0, 0 };

    float A2 = line2point2.y - line2point1.y;
    float B2 = line2point1.x - line2point2.x;

    float C2 = A2 * line2point1.x + B2 * line2point1.y;

    float det = A1[edgeLine] * B2 - A2 * B1[edgeLine];
    var x = (B2 * C1[edgeLine] - B1[edgeLine] * C2) / det;
    var y = (A1[edgeLine] * C2 - A2 * C1[edgeLine]) / det;
    return new Vector3(x, y, 0);

}

bool onScreen(Vector2 input)
{
    return !(input.x > Screen.width || input.x < 0 || input.y > Screen.height || input.y < 0);
}

public class Indicator
{
    public GameObject Target { get; private set; }
    public GameObject PointToTarget { get; private set; }

    public Indicator(GameObject target, GameObject pointToTarget, ObjectTypeEnum type)
    {
        Target = target;
        PointToTarget = pointToTarget;
        var texture = pointToTarget.GetComponentInChildren<UITexture>();
        if (texture != null)
        {
            texture.color = Helper.GetHintColor(type);
        }

    }
}

您可以在更新中调用

foreach (var obj in listIndicator)
        {
            SetIndicatorPosition(obj);
        }