在我的游戏中,玩家的两只手操控中,不想让他们的手围着自己在一个圆形范围内随便乱跑,左手只想让他在左上角,右手在右上角范围活动。所以我制作这样一个算法来实现。
首先用Dot函数划分出4个区域,然后根据区域计算修正后的位置。
先上代码:
hero就是图中蓝色胶囊,hand是黄色的球,红色的newhand是最后修正的位置。如果两个球重叠表示不需要修正,如果修正了红色就是真实算法位置。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class testRange : MonoBehaviour
{public Transform hero; //角色位置public Transform hand; //模型手public Transform newhand; //修正后的位置public float maxDis = 0.6f;protected void Update(){Vector3 dir;//让左手在第二象限。dot是0-1,dot2是-1~0Vector3 atHero = hero.position;Vector3 atHand = hand.position;atHero.y = atHand.y;dir = (atHand - atHero).normalized;float dot = Vector3.Dot(hero.forward, dir);float dot2 = Vector3.Dot(hero.right, dir);Debug.Log(dot + "," + dot2);Vector3 nowat = hand.position;if (dot < 0f){//hand超过身后nowat = GetIntersectWithLineAndPlane(nowat, hero.forward, hero.forward, hero.position);}if (dot2 > 0f){//hand超过右边nowat = GetIntersectWithLineAndPlane(nowat, hero.right, hero.right, hero.position);}//只能围绕身边Vector3 heroat = hero.position;heroat.y = nowat.y;float dis = Vector3.Distance(nowat, heroat);if (dis < maxDis){Vector3 todir = (nowat - heroat).normalized;if (!todir.Equals(Vector3.zero))nowat = heroat + todir * maxDis;else{todir = (heroat - hand.position).normalized;nowat = heroat + todir * maxDis;}}newhand.position = nowat;}/// <summary>/// 计算直线与平面的交点/// </summary>/// <param name="point">直线上某一点</param>/// <param name="direct">直线的方向</param>/// <param name="planeNormal">垂直于平面的的向量</param>/// <param name="planePoint">平面上的任意一点</param>/// <returns></returns>private Vector3 GetIntersectWithLineAndPlane(Vector3 point, Vector3 direct, Vector3 planeNormal, Vector3 planePoint){float d = Vector3.Dot(planePoint - point, planeNormal) / Vector3.Dot(direct.normalized, planeNormal);return d * direct.normalized + point;}
}
上面的代码就是计算hand在第二象限的限制,首先我们观察到dot 在第二象限是 0 ~ 1 的范围,dot2是 -1 ~ 0 ,当超过这个范围了,我们可以通过GetIntersectWithLineAndPlane函数来计算超过这个结界的碰撞点,防止出界。
最后我们可能不希望他跑到玩家某型里面了,我们给一个半径范围maxDis ,让他不要和玩家重叠。
看看效果图。
当我们移动到背后的时候,小球最多和我们平行,当小球移动到右边的时候那么最多知道面前,而且不会进入玩家模型范围。
相交算法链接来自