基于PF规则的CRPG制作尝试(十一)射箭之后箭矢回收的实现
之前(第九节)进行了射箭的实现,也有说目前还有三个部分没有完成。今天我们来完成第三部分,也就是箭矢的回收。
事实上这个问题也困扰了我一段时间,最开始是打算记录箭的初始位置(在角色手上),和父级关系。然后在箭射出2秒后将箭变回记录好的位置,并恢复之前的父子关系(因为箭飞出去的时候是需要解除之前的所属关系的)。
但以上的想法是具有一定问题的,例如:箭矢的初始位置是相对于手的,由于角色一直处于微微晃动的IDLE中,想用世界坐标来记录箭矢的位置是不可能的。
在权衡利弊后,我决定使用Instantiate()函数。通过Instantiate函数复制一个箭矢gameobject。根据我的观察,通过Instantiate函数复制出来的对象的name会默认为原名称+(clone),因此我们可以使用GameObject.Find方法
来对箭进行操作。
具体步骤如下:
在攻击状态(ATTACK)时,对敌方角色点击右键,先将箭调至目前不可见的layer,然后对箭进行复制。之后播放:抽出箭——拉弓——射出动画,在抽出箭的动画中插入了一个事件(Drawarrow),在动画播放到此事件时,将复制出来的箭矢的调至可见的layer。
ActorController部分的代码:
public Animator anim;
public GameObject Weapon;
public GameObject Arrow;
public GameObject target;
public bool isshooting = false;
void Update()
{
if (Input.GetMouseButtonDown(1))
{
//在攻击状态下进行攻击判定
if (state == STATE.ATTACK)
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit) && hit.transform.gameObject.tag == "monster")
{
//角色方向的切换
ActorTrans.LookAt(hit.transform.position);
if (Weapon.tag == "bow" && monsterdis <= 10)
{
ChangeLayer(Arrow.transform, "Weapon");
//复制箭
Instantiate(Arrow, Arrow.transform.parent);
//获得目标物体
target = hit.transform.gameObject;
anim.SetBool("attack", true);
GameObject.Find(Arrow.name + "(Clone)").GetComponent<ArrowData>().enabled = true;
int ar = Attackroll();
if (ar >= hit.transform.gameObject.GetComponent<MonsterData>().AC)
{
int dam = Damage();
print("dur攻击了" + hit.transform.name + " 攻击检定结果为" + ar + " 造成了" + dam + "点伤害");
}
else
print("dur攻击了" + hit.transform.name + "未破防 攻击检定结果为" + ar);
}
}
}
}
}
void Drawarrow()
{
ChangeLayer(GameObject.Find(Arrow.name + "(Clone)").transform, "Actor");
ChangeLayer(Arrow.transform, "Weapon");
print("Drawarrow");
isfiring = true;
}
void arrowshoot()
{
isshooting = true;
Invoke("arrowshootover", 2.0f);
print("射完了");
}
void arrowshootover()
{
isshooting = false;
}
//改变layer
void ChangeLayer(Transform trans, string targetLayer)
{
//遍历更改所有子物体layer
trans.gameObject.layer = LayerMask.NameToLayer(targetLayer);
foreach (Transform child in trans)
{
ChangeLayer(child, targetLayer);
}
}
射箭这部分也没什么值得一提的,无非是在之前的代码中加一段2秒后箭自毁的函数(Destroyarrow())。
ArrowData部分的代码如下:
public ActorController ac;
public const float g = 9.8f;
public GameObject target;
public float speed = 10;
private float verticalSpeed;
private Vector3 moveDirection;
private float angleSpeed;
private float angle;
public GameObject clone;
private float time;
void Start()
{
clone = GameObject.Find(ac.Arrow.name + "(Clone)");
target = ac.target;
float tmepDistance = Vector3.Distance(clone.transform.position, target.transform.position);
float tempTime = tmepDistance / speed;
float riseTime, downTime;
riseTime = downTime = tempTime / 2;
verticalSpeed = g * riseTime;
float tempTan = verticalSpeed / speed;
double hu = Math.Atan(tempTan);
angle = (float)(180 / Math.PI * hu);
clone.transform.eulerAngles = new Vector3(-angle, clone.transform.eulerAngles.y, clone.transform.eulerAngles.z);
angleSpeed = angle / riseTime;
moveDirection = target.transform.position - clone.transform.position;
}
void Update()
{
print("运行了arrowdata");
if ((transform.name == (ac.Arrow.name + "(Clone)")) && ac.isshooting == true)
{
clone.GetComponent<ArrowData>().enabled = true;
clone.transform.parent = null;
clone.transform.LookAt(target.transform.position);
if (clone.transform.position.y < target.transform.position.y)
{
//finish
return;
}
time += Time.deltaTime;
float test = verticalSpeed - g * time;
clone.transform.Translate(moveDirection.normalized * speed * Time.deltaTime, Space.World);
clone.transform.Translate(Vector3.up * test * Time.deltaTime, Space.World);
float testAngle = -angle + angleSpeed * time;
clone.transform.eulerAngles = new Vector3(testAngle, clone.transform.eulerAngles.y, clone.transform.eulerAngles.z);
Invoke("Destroyarrow", 2.0f);
}
}
void Destroyarrow()
{
gameObject.GetComponent<ArrowData>().enabled = false;
Destroy(clone);
print("destroy");
}
以上