創作內容

0 GP

如何讓物件沿著不規則靜態背景移動?

作者:~XD│2017-09-23 20:37:46│巴幣:0│人氣:340
原串:如何讓物件沿著不規則靜態背景移動?

根據山姆遊戲的範例檔做了修改減少震顫的改良版

主要修改的有更新順序、法線的取得方式、以及順便上了很多除錯線XD
雖然還是有點羊顫瘋問題,不過應該比原版本減輕了不少!
跟隨鏡頭雖然還不太OK,不過固定鏡頭的算是可以用了吧_(:D」∠)_



改善震顫的方法我想可以參考多一點的法線加權後達成平滑化
9/25補︰法線加權法試作以後發覺沒用,會顫動是地形顛簸的緣故(・ัω・ั)
我這個只參考兩點很遺憾的震顫還是有點嚴重_(:D」∠)_

另有板友指出可對鏡頭Lerp,這個改良方向應該也很值得嚐試


這是9/24稍作整理的code,有些做法未確定下來,然後開了更多參數玩玩
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class player : MonoBehaviour
{
    public bool isGround;
    public float isGroundDistance = 0.52f;
    //public bool keepGravityPointWhenJumping = false;
    public Vector2 groundNormal;
    Vector3 newRight;
    Rigidbody2D rb;
    SpriteRenderer spriteRenderer;
    PolygonCollider2D pc2d;
    private Vector3 vectorz = new Vector3(0,0,1);
    private Vector3 normalLerped;
    private Vector3 normalLerped2;

    Animator animator;
    public float speed = 3;
    public float alignSpeed = 3;

    public float JumpingForce = 300f;
    public float JumpingDamping = 0.4f;
    public float GravityForce = -9.8f;
    public Vector3 GravityPoint;

    // Use this for initialization
    void Start()
    {
        isGround = false;
        rb = GetComponent<Rigidbody2D>();
        spriteRenderer = GetComponent<SpriteRenderer>();
        animator = GetComponent<Animator>();
        pc2d = GameObject.Find("Land").GetComponent<PolygonCollider2D>();
    }

    // Update is called once per frame
    void Update()
    {
        gravityBias();  // 修正重力方向
        alignSurface(); // 讓角色對齊碰觸的地面
        groundTest();   // 檢測角色是否正在地面?
        jumpPose();     // 依據角色狀態播放跳躍動態
    }

    private void gravityBias()
    {
        //if(!keepGravityPointWhenJumping || isGround)
            GravityPoint = getClosetPointonEdge(pc2d);
        // Physics2D.gravity = Vector3.Normalize(transform.position - p) * (-9.8f);
        Vector2 one = Vector3.Normalize(transform.position - GravityPoint) * GravityForce * Time.deltaTime;
        rb.velocity += (one * (1+Time.deltaTime));
    }

    int GetClosestPointIndex(PolygonCollider2D polygonCollider2D)
    {
        Vector2[] points = polygonCollider2D.points;
        int ClosestPointIndex = 0;
        float closestDist = Mathf.Infinity;
        for (var i = 0; i < points.Length; i++)
        {
            var worldSpaceVertex = polygonCollider2D.transform.TransformPoint(points[i]);
            float currentDist = Vector3.Distance(worldSpaceVertex, transform.position);

            if (currentDist < closestDist)
            {
                closestDist = currentDist;
                ClosestPointIndex = i;
            }
        }
        return ClosestPointIndex;
    }

    Vector3 getClosetPointonEdge(PolygonCollider2D PolygonCollider2D)
    {
        Vector3 result = Vector3.zero;
        Vector2[] points = PolygonCollider2D.points;
        var index = GetClosestPointIndex(PolygonCollider2D);
        int index_next = index == points.Length - 1 ? 0 : index + 1;
        int index_last = index == 0 ? points.Length - 1 : index - 1;
        Vector3 point = PolygonCollider2D.transform.TransformPoint(points[index]);
        Vector3 point_next = PolygonCollider2D.transform.TransformPoint(points[index_next]);
        Vector3 point_last = PolygonCollider2D.transform.TransformPoint(points[index_last]);
        Vector3 a = ClosestPointToLine(point, point_next, transform.position);
        Vector3 b = ClosestPointToLine(point, point_last, transform.position);
        result = Vector3.Distance(transform.position, a) > Vector3.Distance(transform.position, b) ? b : a;

        Vector3 normal_next = Vector3.Cross(point_next-point, vectorz).normalized;
        Vector3 normal_last = Vector3.Cross(point-point_last, vectorz).normalized;
        Debug.DrawRay(point_next, normal_next, Color.white);
        Debug.DrawRay(point_last, normal_last, Color.white);

        float normalLerp;
        if(Vector3.Distance(transform.position, a) > Vector3.Distance(transform.position, b))
        {    // in point_last
            normalLerp = Vector3.Distance(result, point) / Vector3.Distance(point_last, point);
        }
        else
        {    // in point_next
            normalLerp = Vector3.Distance(result, point_next) / Vector3.Distance(point, point_next);
        }
        normalLerped = Vector3.Lerp(normal_last, normal_next, normalLerp);

        // project by hand
        //normalLerped2 = (point_next-point_last)*(Vector3.Distance(Vector3.zero,result-point_last)/Vector3.Distance(Vector3.zero,point_next-point_last))*Vector3.Dot((result-point_last).normalized, (point_next-point_last).normalized);
        normalLerped2 = Vector3.Project(result-point_last, point_next-point_last);

        float lenChara = Vector3.Distance(point_last, normalLerped2);
        float lenCorner = Vector3.Distance(point_last, Vector3.Project(point-point_last,point_next-point_last));
        float lenTotal = Vector3.Distance(point_last, point_next-point_last);
        if(lenChara > lenCorner)    normalLerp = (lenChara-lenCorner)/(lenTotal-lenCorner);
        else                        normalLerp = lenChara / lenCorner;
        
        Debug.DrawRay(point_last, point_next-point_last, Color.gray);
        Debug.DrawRay(point_last, normalLerped2, Color.white);

        normalLerped2 = Vector3.Lerp(normal_last, normal_next, Vector3.Distance(point_last, result) / Vector3.Distance(result, point_next));
        //normalLerped2 = Vector3.Lerp(normal_last, normal_next, normalLerp);

        Debug.DrawRay(result, normalLerped2 *1.3f, Color.red);
        Debug.DrawRay(result, normalLerped*1.2f, Color.white);

        normalLerped = normalLerped2;

        Debug.DrawRay(point_last, point-point_last, Color.yellow);
        Debug.DrawRay(point, point_next-point, Color.green);

        Debug.DrawRay(result, Vector3.zero-result, Color.red);

        return result;
    }

    Vector3 ClosestPointToLine(Vector3 _lineStartPoint, Vector3 _lineEndPoint, Vector3 _testPoint)
    {
        Vector3 pointTowardStart = _testPoint - _lineStartPoint;
        Vector3 startTowardEnd = (_lineEndPoint - _lineStartPoint).normalized;

        Debug.DrawRay(_lineStartPoint, pointTowardStart, Color.magenta);
        Debug.DrawRay(_lineStartPoint, startTowardEnd, Color.cyan);

        float lengthOfLine = Vector3.Distance(_lineStartPoint, _lineEndPoint);
        float dotProduct = Vector3.Dot(startTowardEnd, pointTowardStart);

        if (dotProduct <= 0)
        {
            return _lineStartPoint;
        }

        if (dotProduct >= lengthOfLine)
        {
            return _lineEndPoint;
        }

        Vector3 thirdVector = startTowardEnd * dotProduct;

        Vector3 closestPointOnLine = _lineStartPoint + thirdVector;

        return closestPointOnLine;
    }
    void alignSurface()
    {
        //Vector3 normal = getGroundSurface().normal;
        Vector3 normal = normalLerped;
        Vector3 newDir = Vector3.RotateTowards(transform.up, normal, /*0.05f*/ alignSpeed * Time.deltaTime , 0.0F);
        Debug.DrawRay(transform.position, newDir, Color.blue);
        transform.rotation = Quaternion.FromToRotation(Vector3.up, newDir);
    }

    void jumpPose()
    {
        if (isGround && !animator.GetCurrentAnimatorStateInfo(0).IsName("player@run")) animator.Play("player@idle");// 如果角色在地面並且未播放跑步動作,則播放待機動作
        if (!isGround) animator.Play("player@jump");// 如果角色在空中,則播放跳躍動作
    }

    void groundTest()
    {
        isGround = Vector3.Distance(transform.position, GravityPoint) < isGroundDistance;
    }

    RaycastHit2D getGroundSurface()
    {
        var raycasthit2d = Physics2D.Raycast(transform.position + Vector3.down * 0.01f, GravityPoint - transform.position, 5f);
        return raycasthit2d;
    }

    public void goLeft()
    {
        transform.position -= transform.right * speed * (1+Time.deltaTime);
        spriteRenderer.flipX = true;
        if (isGround) animator.Play("player@run");
    }
    public void goRight()
    {
        transform.position += transform.right * speed * (1+Time.deltaTime);
        spriteRenderer.flipX = false;
        if (isGround) animator.Play("player@run");
    }
    public void goJump()
    {
        if (isGround) rb.AddForce(transform.up * JumpingForce);
    }
    public void release()
    {
        rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * JumpingDamping); //放開空白鍵 減緩跳躍上升
    }
}


**這是9/23舊版的**
原本想說整理簡化一下再發上來,弄著弄著還是整坨丟上來好了
裡面有實驗用廢碼參考前注意一下_(:D」∠)_
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class player : MonoBehaviour
{
    public bool isGround;
    public Vector2 groundNormal;
    Vector3 newRight;
    Rigidbody2D rb;
    SpriteRenderer spriteRenderer;
    PolygonCollider2D pc2d;
    Vector3 vectorz = new Vector3(0,0,1);
    Vector3 normalLerped;
    Vector3 normalLerped2;

    Animator animator;
    public float speed = 3;
    public float alignSpeed = 3;

    public Vector3 GravityPoint;

    public Vector3 ClosestPointToLineA = Vector3.zero;
    public Vector3 ClosestPointToLineB = Vector3.zero;

    // Use this for initialization
    void Start()
    {
        isGround = false;
        rb = GetComponent<Rigidbody2D>();
        spriteRenderer = GetComponent<SpriteRenderer>();
        animator = GetComponent<Animator>();
        pc2d = GameObject.Find("Land").GetComponent<PolygonCollider2D>();
    }

    // Update is called once per frame
    void Update()
    {
        gravityBias();  // 修正重力方向
        alignSurface(); // 讓角色對齊碰觸的地面
        groundTest();   // 檢測角色是否正在地面?
        jumpPose();     // 依據角色狀態播放跳躍動態
    }

    private void gravityBias()
    {
        GravityPoint = getClosetPointonEdge(pc2d);
        // Physics2D.gravity = Vector3.Normalize(transform.position - p) * (-9.8f);
        Vector2 one = Vector3.Normalize(transform.position - GravityPoint) * (-9.8f) * Time.deltaTime;
        rb.velocity += one;
    }

    int GetClosestPointIndex(PolygonCollider2D polygonCollider2D)
    {
        Vector2[] points = polygonCollider2D.points;
        int ClosestPointIndex = 0;
        float closestDist = Mathf.Infinity;
        for (var i = 0; i < points.Length; i++)
        {
            var worldSpaceVertex = polygonCollider2D.transform.TransformPoint(points[i]);
            float currentDist = Vector3.Distance(worldSpaceVertex, transform.position);

            if (currentDist < closestDist)
            {
                closestDist = currentDist;
                ClosestPointIndex = i;
            }
        }
        return ClosestPointIndex;
    }

    Vector3 getClosetPointonEdge(PolygonCollider2D PolygonCollider2D)
    {
        Vector3 result = Vector3.zero;
        Vector2[] points = PolygonCollider2D.points;
        var index = GetClosestPointIndex(PolygonCollider2D);
        int index_next = index == points.Length - 1 ? 0 : index + 1;
        int index_last = index == 0 ? points.Length - 1 : index - 1;
        Vector3 point = PolygonCollider2D.transform.TransformPoint(points[index]);
        Vector3 point_next = PolygonCollider2D.transform.TransformPoint(points[index_next]);
        Vector3 point_last = PolygonCollider2D.transform.TransformPoint(points[index_last]);
        var a = ClosestPointToLine(point, point_next, transform.position);
        var b = ClosestPointToLine(point, point_last, transform.position);
        result = Vector3.Distance(transform.position, a) > Vector3.Distance(transform.position, b) ? b : a;

        Vector3 normal_next = Vector3.Cross(point_next-point, vectorz).normalized;
        Vector3 normal_last = Vector3.Cross(point-point_last, vectorz).normalized;
        Debug.DrawRay(point_next, normal_next, Color.white);
        Debug.DrawRay(point_last, normal_last, Color.white);
        float normalLerp;
        if(Vector3.Distance(transform.position, a) > Vector3.Distance(transform.position, b))
        {    // in point_last
            normalLerp = Vector3.Distance(result, point) / Vector3.Distance(point_last, point);
            //normalLerp = Vector3.Distance(point_last, result) / Vector3.Distance(result, point_next);

        }
        else
        {    // in point_next
            normalLerp = Vector3.Distance(result, point_next) / Vector3.Distance(point, point_next);
        }
        //Debug.DrawRay(point_next, (point_next-point_last)*Vector3.Dot(point_next-point_last, result-point_last), Color.white);
        normalLerped = Vector3.Lerp(normal_last, normal_next, normalLerp);
        normalLerped2 = Vector3.Lerp(normal_last, normal_next, Vector3.Distance(point_last, result) / Vector3.Distance(result, point_next));
        Debug.DrawRay(result, normalLerped2 *1.3f, Color.red);
        Debug.DrawRay(result, normalLerped*1.2f, Color.white);
        normalLerped = normalLerped2;
        ClosestPointToLineA = a;
        ClosestPointToLineA.z = Vector3.Distance(transform.position, a);
        ClosestPointToLineB = b;
        ClosestPointToLineB.z = Vector3.Distance(transform.position, b);

        Debug.DrawRay(point_last, point-point_last, Color.yellow);
        Debug.DrawRay(point, point_next-point, Color.green);

        Debug.DrawRay(result, Vector3.zero-result, Color.red);

        return result;
    }

    Vector3 ClosestPointToLine(Vector3 _lineStartPoint, Vector3 _lineEndPoint, Vector3 _testPoint)
    {
        Vector3 pointTowardStart = _testPoint - _lineStartPoint;
        Vector3 startTowardEnd = (_lineEndPoint - _lineStartPoint).normalized;

        Debug.DrawRay(_lineStartPoint, pointTowardStart, Color.magenta);
        Debug.DrawRay(_lineStartPoint, startTowardEnd, Color.cyan);

        float lengthOfLine = Vector3.Distance(_lineStartPoint, _lineEndPoint);
        float dotProduct = Vector3.Dot(startTowardEnd, pointTowardStart);

        if (dotProduct <= 0)
        {
            return _lineStartPoint;
        }

        if (dotProduct >= lengthOfLine)
        {
            return _lineEndPoint;
        }

        Vector3 thirdVector = startTowardEnd * dotProduct;

        Vector3 closestPointOnLine = _lineStartPoint + thirdVector;

        return closestPointOnLine;
    }
    void alignSurface()
    {
        //Vector3 normal = getGroundSurface().normal;
        Vector3 normal = normalLerped;
        Vector3 newDir = Vector3.RotateTowards(transform.up, normal, /*0.05f*/ alignSpeed * Time.deltaTime , 0.0F);
        Debug.DrawRay(transform.position, newDir, Color.blue);
        transform.rotation = Quaternion.FromToRotation(Vector3.up, newDir);
    }

    void jumpPose()
    {
        if (isGround && !animator.GetCurrentAnimatorStateInfo(0).IsName("player@run")) animator.Play("player@idle");// 如果角色在地面並且未播放跑步動作,則播放待機動作
        if (!isGround) animator.Play("player@jump");// 如果角色在空中,則播放跳躍動作
    }

    void groundTest()
    {
        isGround = Vector3.Distance(transform.position, GravityPoint) < 0.5f;
    }

    RaycastHit2D getGroundSurface()
    {
        var raycasthit2d = Physics2D.Raycast(transform.position + Vector3.down * 0.01f, GravityPoint - transform.position, 5f);
        return raycasthit2d;
    }

    public void goLeft()
    {
        transform.position -= transform.right * speed * (1+Time.deltaTime);
        spriteRenderer.flipX = true;
        if (isGround) animator.Play("player@run");
    }
    public void goRight()
    {
        transform.position += transform.right * speed * (1+Time.deltaTime);
        spriteRenderer.flipX = false;
        if (isGround) animator.Play("player@run");
    }
    public void goJump()
    {
        if (isGround) rb.AddForce(transform.up * 300);
    }
    public void release()
    {
        rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * 0.5f); //放開空白鍵 減緩跳躍上升
    }
}

引用網址:https://home.gamer.com.tw/TrackBack.php?sn=3732100
All rights reserved. 版權所有,保留一切權利

相關創作

留言共 0 篇留言

我要留言提醒:您尚未登入,請先登入再留言

喜歡★hjdmeury 可決定是否刪除您的留言,請勿發表違反站規文字。

前一篇:Google試算表套用表... 後一篇:「式姫草子」即將結束服務...

追蹤私訊切換新版閱覽

作品資料夾

robert286 ლ(´•д• ̀ლ
ლ(´•д• ̀ლ看更多我要大聲說1小時前


face基於日前微軟官方表示 Internet Explorer 不再支援新的網路標準,可能無法使用新的應用程式來呈現網站內容,在瀏覽器支援度及網站安全性的雙重考量下,為了讓巴友們有更好的使用體驗,巴哈姆特即將於 2019年9月2日 停止支援 Internet Explorer 瀏覽器的頁面呈現和功能。
屆時建議您使用下述瀏覽器來瀏覽巴哈姆特:
。Google Chrome(推薦)
。Mozilla Firefox
。Microsoft Edge(Windows10以上的作業系統版本才可使用)

face我們了解您不想看到廣告的心情⋯ 若您願意支持巴哈姆特永續經營,請將 gamer.com.tw 加入廣告阻擋工具的白名單中,謝謝 !【教學】