写在前面
实现类似于愤怒的小鸟中的弹弓功能。
功能
- 拖拽小鸟松开时发射
- 拖拽范围的限制
实现
-
首先要用到Unity中的一个物理组件,Spring Joint 2D。它就是一个弹簧连接,就相当于两个物体间用弹簧连接一样。不管两个物体大于或者小于某固定的距离,都会产生相互作用的力。而且伴有弹性系数等。具体属性如下图(001):
让物体跟随鼠标移动,因为鼠标是屏幕坐标系,需要转化为世界坐标系才能赋值使用。由于是2D场景,在转化后要修改下z轴,不然会看不到物体,代码如下:
transform.position = Camera.main.ScreenToWorldPoint(Input.mousePosition);
transform.position += new Vector3(0, 0, 10);
- 之后给拖拽限定一个范围,让物体在有效的范围内移动。对此,在弹弓上(Sprite图)上创建一个节点(CenterObject),计算出物体到节点的方向,在定义一个距离最大值。
if (Vector3.Distance(transform.position, CenterObject.position) > MaxDistance) {
Vector3 pos1 = (transform.position - CenterObject.position).normalized;
pos1 *= MaxDistance;
transform.position = CenterObject.position + pos1;
}
重点讨论
愤怒的小鸟的弹弓功能有一个小细节,就是拖拽物体碰到弓柄的时候不会透过弓柄,而是往上拉到发射口,这样显得更加真实,大致实现方法如下:
1 . 在会发生碰撞的地方设置碰撞体,设置IsTrigger为true。
2 . 添加监听事件
void OnTriggerEnter2D(Collider2D collider)
{
IsTrigger = true;
}
void OnTriggerExit2D(Collider2D collider)
{
IsTrigger = false;
}
3 . 当触发碰撞体时,将拖拽最大距离缩小,将其拉到发射口。当离开碰撞体时,再将距离拉大。注意,以下代码中第一个if代码块,当物体运动到发射口时,且此时不再触发碰撞体,就不会缩短距离了,反之增加距离,这是受跟随鼠标的影响往下移动又触发碰撞体,产生了抖动的情况。要避开这种情况,就是当物体处于发射口时不要增加距离。
void OnSetpOver()
{
if (Vector3.Distance(transform.position, ButtomObject.position) < 0.2)
{
IsAddDistance = false;
}
else
IsAddDistance = true;
if (IsTrigger)
{
if (IsMove)
MaxDistance -= 0.1f;
}
else
{
if (IsMove && MaxDistance< 0.9f && IsAddDistance)
{
MaxDistance += 0.1f;
}
}
}
功能源码
文末有项目下载地址,结合项目更容易理解。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Brid : MonoBehaviour {
//鼠标是否按下
private bool IsClick;
//是否触发了碰撞体
private bool IsTrigger;
//鼠标是否在移动
private bool IsMove;
//是否允许拉长距离
private bool IsAddDistance;
//能够拖拽小鸟的最长距离
public float MaxDistance;
//记录鼠标0.02s前的位置
private Vector3 firstpos;
//计时
private float timer = 0f;
private SpringJoint2D Sp;
private Rigidbody2D rig2d;
public CapsuleCollider2D TargetCollider;
public LineRenderer right;
public Transform Rightobject;
public Transform ButtomObject;
public Transform Target;
public Transform CenterObject;
public LineRenderer left;
public Transform Leftobject;
void Awake()
{
Sp = GetComponent<SpringJoint2D> ();
rig2d = GetComponent<Rigidbody2D> ();
}
void OnMouseDown()
{
IsClick = true;
rig2d.isKinematic = true;
}
void OnMouseUp()
{
IsClick = false;
rig2d.isKinematic = false;
TargetCollider.enabled = false;
Invoke("Fly", 0.1f);
}
void OnTriggerEnter2D(Collider2D collider)
{
IsTrigger = true;
}
void OnTriggerExit2D(Collider2D collider)
{
IsTrigger = false;
}
void FixedUpdate ()
{
if (IsClick)
{
transform.position = Camera.main.ScreenToWorldPoint(Input.mousePosition);
transform.position += new Vector3(0, 0, 10);
if (Vector3.Distance(transform.position, CenterObject.position) > MaxDistance) {
Vector3 pos1 = (transform.position - CenterObject.position).normalized;
pos1 *= MaxDistance;
transform.position = CenterObject.position + pos1;
}
Line();
OnSetpOver();
}
OnMouseMove();
}
void Fly()
{
Sp.enabled = false;
}
void Line()
{
right.SetPosition (0, Rightobject.position);
right.SetPosition (1, transform.position);
left.SetPosition (0, Leftobject.position);
left.SetPosition (1, transform.position);
}
void Cancel()
{
}
void OnMouseMove()
{
if (timer < 0.02f)
{
firstpos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
timer += Time.fixedDeltaTime;
}
else
{
if (firstpos != Camera.main.ScreenToWorldPoint(Input.mousePosition))
{
IsMove = true;
}
else
IsMove = false;
timer = 0;
}
}
void OnSetpOver()
{
if (Vector3.Distance(transform.position, ButtomObject.position) < 0.2)
{
IsAddDistance = false;
}
else
IsAddDistance = true;
if (IsTrigger)
{
if (IsMove)
MaxDistance -= 0.1f;
}
else
{
if (IsMove && MaxDistance< 0.9f && IsAddDistance)
{
MaxDistance += 0.1f;
}
}
}
}
结语
以上代码实现的拖动看起来不是很顺畅,更真正的愤怒的小鸟比起来显得有些僵硬,如果读者有更好的解决方案请告诉我。