VRTK_PositionRewind脚本简析(VRTK_v3.3.0版)

尝试在眼镜碰撞事件时将游戏区域的位置倒回到最后一个已知的有效位置
将VRTK_PositionRewind_UnityEvents组件添加到VRTK_PositionRewind对象允许访问UnityEvents它将对类事件做出相同的反应。所有C#委托事件都映射到带有On前缀的Unity事件。例如MyEvent- > OnMyEvent。

必备组建

VRTK_BodyPhysics - Body Physics脚本,用于管理场景中身体存在的碰撞。
VRTK_HeadsetCollision - 耳机碰撞脚本,用于确定耳机何时与有效几何体发生碰撞。

结构体 PositionRewindEventArgs

Vector3 collidedPosition 碰撞体位置
Vector3 resetPosition 恢复位置

委托

public delegate void PositionRewindEventHandler(object sender, PositionRewindEventArgs e);

参数:当前object 上述结构体

枚举

CollisionDetectors 有效的碰撞检测器
有三种情况 HeadsetOnly 仅侦听耳机对撞机上的碰撞
BodyOnly 仅侦听身体物理对撞机上的碰撞。
HeadsetAndBody 侦听取耳机对撞机和身体物理对撞机上的碰撞。

字段介绍

见代码注释

事件

public event PositionRewindEventHandler PositionRewindToSafe;

方法

OnPositionRewindToSafe(PositionRewindEventArgs e) 执行事件的方法
SetLastGoodPosition() 存储当前有效的播放区域和耳机位置
RewindPosition() 将播放区域位置重置为播放区域的最后已知良好位置。

私有方法

见代码注释

 // Position Rewind|Presence|70070
namespace VRTK
  {
using UnityEngine;

/// <summary>
/// Event Payload
/// </summary>
/// <param name="collidedPosition">The position of the play area when it collded.</param>
/// <param name="resetPosition">The position of the play area when it has been rewinded to a safe position.</param>
public struct PositionRewindEventArgs
{
    public Vector3 collidedPosition;//碰撞位置
    public Vector3 resetPosition;//安全位置
}

/// <summary>
/// Event Payload
/// </summary>
/// <param name="sender">this object</param>
/// <param name="e"><see cref="PositionRewindEventArgs"/></param>
public delegate void PositionRewindEventHandler(object sender, PositionRewindEventArgs e);

/// <summary>
/// Attempts to rewind the position of the play area to a last know valid position upon the headset collision event.
/// </summary>
/// <remarks>
/// **Required Components:**
///  * `VRTK_BodyPhysics` - A Body Physics script to manage the collisions of the body presence within the scene.
///  * `VRTK_HeadsetCollision` - A Headset Collision script to determine when the headset is colliding with valid geometry.
///
/// **Script Usage:**
///  * Place the `VRTK_PositionRewind` script on any active scene GameObject.
/// </remarks>
/// <example>
/// `VRTK/Examples/017_CameraRig_TouchpadWalking` has the position rewind script to reset the user's position if they walk into objects.
/// </example>
[AddComponentMenu("VRTK/Scripts/Presence/VRTK_PositionRewind")]
public class VRTK_PositionRewind : MonoBehaviour
{
    /// <summary>
    /// Valid collision detectors.
    /// </summary>
    public enum CollisionDetectors
    {
        /// <summary>
        /// Listen for collisions on the headset collider only.
        /// </summary>
        HeadsetOnly,
        /// <summary>
        /// Listen for collisions on the body physics collider only.
        /// </summary>
        BodyOnly,
        /// <summary>
        /// Listen for collisions on both the headset collider and body physics collider.
        /// </summary>
        HeadsetAndBody
    }

    [Header("Rewind Settings")]

    [Tooltip("The colliders to determine if a collision has occured for the rewind to be actioned.")]
    public CollisionDetectors collisionDetector = CollisionDetectors.HeadsetOnly;//上述的枚举
    [Tooltip("If this is checked then the collision detector will ignore colliders set to `Is Trigger = true`.")]
    public bool ignoreTriggerColliders = false;//是否忽略Is Trigger = true
    [Tooltip("The amount of time from original headset collision until the rewind to the last good known position takes place.")]
    public float rewindDelay = 0.5f;//时间 从原始耳机碰撞到倒回到最后一个已知位置的时间量
    [Tooltip("The additional distance to push the play area back upon rewind to prevent being right next to the wall again.")]
    public float pushbackDistance = 0.5f;//距离 在倒带时将游戏区域推回的额外距离,以防止再次靠近墙壁。
    [Tooltip("The threshold to determine how low the headset has to be before it is considered the user is crouching. The last good position will only be recorded in a non-crouching position.")]
    public float crouchThreshold = 0.5f;确定耳机在被认为是蹲伏之前必须有多低的阈值。最后一个好位置只会记录在非蹲伏位置。
    [Tooltip("The threshold to determind how low the headset can be to perform a position rewind. If the headset Y position is lower than this threshold then a rewind won't occur.")]
    public float crouchRewindThreshold = 0.1f;确定耳机执行位置倒带的程度的阈值。如果耳机Y位置低于此阈值,则不会发生倒带。
    [Tooltip("A specified VRTK_PolicyList to use to determine whether any objects will be acted upon by the Position Rewind.")]
    public VRTK_PolicyList targetListPolicy;//目标列表策略 用于确定位置回滚是否将对任何对象执行操作。

    [Header("Custom Settings")]

    [Tooltip("The VRTK Body Physics script to use for the collisions and rigidbodies. If this is left blank then the first Body Physics script found in the scene will be used.")]
    public VRTK_BodyPhysics bodyPhysics;//用于碰撞和刚体的VRTK身体物理学脚本。如果将其留空,则将使用场景中找到的第一个Body Physics脚本。
    [Tooltip("The VRTK Headset Collision script to use to determine if the headset is colliding. If this is left blank then the script will need to be applied to the same GameObject.")]
    public VRTK_HeadsetCollision headsetCollision;//用于确定耳机是否发生碰撞的VRTK耳机碰撞脚本。如果将其留空,则需要将脚本应用于同一GameObject。

  //事件
    public event PositionRewindEventHandler PositionRewindToSafe;
   //私有字段
    protected Transform headset;
    protected Transform playArea;

    protected Vector3 lastGoodStandingPosition;
    protected Vector3 lastGoodHeadsetPosition;
    protected float highestHeadsetY;
    protected float lastPlayAreaY;
    protected bool lastGoodPositionSet = false;
    protected bool hasCollided = false;
    protected bool isColliding = false;
    protected bool isRewinding = false;
    protected float collideTimer = 0f;

    public virtual void OnPositionRewindToSafe(PositionRewindEventArgs e)
    {
        if (PositionRewindToSafe != null)
        {
            PositionRewindToSafe(this, e);
        }
    }

    /// <summary>
    /// The SetLastGoodPosition method stores the current valid play area and headset position.
    /// </summary>
    public virtual void SetLastGoodPosition()
    {
        if (playArea != null && headset != null)
        {
            lastGoodPositionSet = true;
            lastGoodStandingPosition = playArea.position;
            lastGoodHeadsetPosition = headset.position;
        }
    }

    /// <summary>
    /// The RewindPosition method resets the play area position to the last known good position of the play area.
    /// </summary>
    public virtual void RewindPosition()
    {
        if (headset != null)
        {
            Vector3 storedPosition = playArea.position;
            Vector3 resetVector = lastGoodHeadsetPosition - headset.position;
            Vector3 moveOffset = resetVector.normalized * pushbackDistance;
            playArea.position += resetVector + moveOffset;
            if (bodyPhysics != null)
            {
                bodyPhysics.ResetVelocities();
            }
            OnPositionRewindToSafe(SetEventPayload(storedPosition));
        }
    }

    protected virtual void Awake()//Add
    {
        VRTK_SDKManager.AttemptAddBehaviourToToggleOnLoadedSetupChange(this);
    }

    protected virtual void OnEnable()
    {
        lastGoodPositionSet = false;//
        headset = VRTK_DeviceFinder.HeadsetTransform();
        playArea = VRTK_DeviceFinder.PlayAreaTransform();//获取
        bodyPhysics = (bodyPhysics != null ? bodyPhysics : FindObjectOfType<VRTK_BodyPhysics>());//获取BodyPhysics
        headsetCollision = (headsetCollision != null ? headsetCollision : GetComponentInChildren<VRTK_HeadsetCollision>());//获取VRTK_HeadsetCollision
        ManageListeners(true);
    }

    protected virtual void OnDisable()
    {
        ManageListeners(false);
    }

    protected virtual void OnDestroy()//Remove
    {
        VRTK_SDKManager.AttemptRemoveBehaviourToToggleOnLoadedSetupChange(this);
    }

    protected virtual void Update()
    {
        if (isColliding)
        {
            if (collideTimer > 0f)
            {
                collideTimer -= Time.deltaTime;
            }
            else
            {
                collideTimer = 0f;
                isColliding = false;
                DoPositionRewind();
            }
        }
    }

    protected virtual PositionRewindEventArgs SetEventPayload(Vector3 previousPosition)
    {
        PositionRewindEventArgs e;
        e.collidedPosition = previousPosition;
        e.resetPosition = playArea.position;
        return e;
    }

    protected virtual bool CrouchThresholdReached()
    {
        float floorVariant = 0.005f;
        return (playArea.position.y > (lastPlayAreaY + floorVariant) || playArea.position.y < (lastPlayAreaY - floorVariant));
    }

    protected virtual void SetHighestHeadsetY()
    {
        highestHeadsetY = (CrouchThresholdReached() ? crouchThreshold : (headset.localPosition.y > highestHeadsetY) ? headset.localPosition.y : highestHeadsetY);
    }

    protected virtual void UpdateLastGoodPosition()
    {
        float highestYDiff = highestHeadsetY - crouchThreshold;
        if (headset.localPosition.y > highestYDiff && highestYDiff > crouchThreshold)
        {
            SetLastGoodPosition();
        }
        lastPlayAreaY = playArea.position.y;
    }

    protected virtual void FixedUpdate()
    {
        if (!isColliding && playArea != null)
        {
            SetHighestHeadsetY();
            UpdateLastGoodPosition();
        }
    }
 //
    protected virtual void StartCollision(GameObject target, Collider collider)
    {
        if (ignoreTriggerColliders && collider.isTrigger)
        {
            return;
        }

        if (!VRTK_PolicyList.Check(target, targetListPolicy))
        {
            isColliding = true;
            if (!hasCollided && collideTimer <= 0f)
            {
                hasCollided = true;
                collideTimer = rewindDelay;
            }
        }
    }

    protected virtual void EndCollision(Collider collider)
    {
        if (ignoreTriggerColliders && collider != null && collider.isTrigger)
        {
            return;
        }

        isColliding = false;
        hasCollided = false;
        isRewinding = false;
    }

    protected virtual bool BodyCollisionsEnabled()
    {
        return (bodyPhysics == null || bodyPhysics.enableBodyCollisions);
    }

    protected virtual bool CanRewind()
    {
        return (!isRewinding && playArea != null & lastGoodPositionSet && headset.localPosition.y > crouchRewindThreshold && BodyCollisionsEnabled());
    }

    protected virtual void DoPositionRewind()
    {
        if (CanRewind())
        {
            isRewinding = true;
            RewindPosition();
        }
    }

    protected virtual bool HeadsetListen()
    {
        return (collisionDetector == CollisionDetectors.HeadsetAndBody || collisionDetector == CollisionDetectors.HeadsetOnly);
    }

    protected virtual bool BodyListen()
    {
        return (collisionDetector == CollisionDetectors.HeadsetAndBody || collisionDetector == CollisionDetectors.BodyOnly);
    }

    protected virtual void ManageListeners(bool state)
    {
        if (state)
        {
            if (headsetCollision != null && HeadsetListen())
            {
         //注册headsetCollision的事件
                headsetCollision.HeadsetCollisionDetect += HeadsetCollisionDetect;
                headsetCollision.HeadsetCollisionEnded += HeadsetCollisionEnded;
            }
            if (bodyPhysics != null && BodyListen())
            {
    //注册bodyPhysics的事件
                bodyPhysics.StartColliding += StartColliding;
                bodyPhysics.StopColliding += StopColliding;
            }
        }
        else
        {
            if (headsetCollision != null && HeadsetListen())
            {
    //移除注册
                headsetCollision.HeadsetCollisionDetect -= HeadsetCollisionDetect;
                headsetCollision.HeadsetCollisionEnded -= HeadsetCollisionEnded;
            }
            if (bodyPhysics != null && BodyListen())
            {
                bodyPhysics.StartColliding -= StartColliding;
                bodyPhysics.StopColliding -= StopColliding;
            }
        }
    }

    private void StartColliding(object sender, BodyPhysicsEventArgs e)
    {
        StartCollision(e.target, e.collider);
    }

    private void StopColliding(object sender, BodyPhysicsEventArgs e)
    {
        EndCollision(e.collider);
    }
    //
    protected virtual void HeadsetCollisionDetect(object sender, HeadsetCollisionEventArgs e)
    {
        StartCollision(e.collider.gameObject, e.collider);
    }
   //
    protected virtual void HeadsetCollisionEnded(object sender, HeadsetCollisionEventArgs e)
    {
        EndCollision(e.collider);
    }
}
 }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容