您的位置:首页 > 移动开发 > Unity3D

我的Unity 3D之旅——第一人称射击游戏(6)

2016-09-02 21:12 806 查看

七、交互

现在敌人已经会自动寻路并且攻击主角了,但并不能造成实际伤害,主角也还不能攻击敌人。接下来我们就分别为主角和敌人的脚本添加处理代码来实现主角和敌人之间的交互。

1. 打开脚本Player.cs,添加OnDamage方法来处理主角被攻击时的逻辑。

/// <summary>
/// 主角被攻击时触发的逻辑代码
/// </summary>
/// <param name="damage">伤害数值</param>
public void OnDamage(int damage)
{
life -= damage;
// 更新UI生命值
GameManager.Instance.SetLife(life);
// 如果生命为0,游戏结束,取消鼠标锁定
if (life <= 0)
{
Cursor.lockState = CursorLockMode.None;
}
}

2. 在Player类中添加几个属性,为射击做准备。

public LayerMask layer; // 射击时射线能射到的碰撞层
public Transform fx; // 射中目标后的粒子效果
public AudioClip clip;  // 射击音效

private Transform muzzlePoint; // 枪口的Transform组件
private float shootTimer = 0; // 射击间隔的计时器


3. 在Start方法中添加如下代码,获取代表枪口的Transform组件。在预设好的武器模型中有一个叫muzzlepoint的空游戏体,它是在3D建模软件中加进去的,用来标识枪口的位置。在查找它时,由于它处于较深的层级,层级与层级的名字要用“/”分隔。

// 获取枪口
muzzlePoint = cameraTransform.FindChild("M16/weapon/muzzlepoint").GetComponent<Transform>();

4. 在Update方法中添加如下代码,使用Physics.Raycast射出一条射线来模拟射击功能,如果射线与敌人相碰撞则使敌人减少一定生命值。此时由于Enemy类中还未定义OnDamage方法,因此在试图调用时会报错。暂时忽略这个问题,在我们为Enemy类添加OnDamage方法的相应代码后这个问题就不存在了。

// 更新射击间隔时间
shootTimer -= Time.deltaTime;
// 鼠标左键射击
if (Input.GetMouseButton(0) && shootTimer <= 0)
{
shootTimer = 0.1f;
// 播放射击音效
GetComponent<AudioSource>().PlayOneShot(clip);
// 更新UI,减少弹药数量
GameManager.Instance.SetAmmo(1);
// 用一个RaycastHit对象保存射线的碰撞结果
RaycastHit info;
// 从枪口所在位置向摄像机面向的正前方发出一条射线
// 该射线只与layer指定的层发生碰撞
if (Physics.Raycast(muzzlePoint.position,
cameraTransform.TransformDirection(Vector3.forward),
out info, 100, layer))
{
// 判断是否射中Tag为enemy的物体
if (info.transform.tag.Equals("enemy"))
{
// 敌人减少生命
info.transform.GetComponent<Enemy>().OnDamage(1);
}
}
// 在射中的地方释放一个粒子效果
Instantiate(fx, info.point, info.transform.rotation);
}

</pre><p>5. 添加两个碰撞层enemy和level,将enemy层指定给敌人,level层指定给场景模型。然后再创建一个Tag,命名为enemy并指定给敌人。</p><p><img src="https://img-blog.csdn.net/20160903163257577" alt="" /></p><p>6. 选中主角的游戏体,为其添加Audio Source组件。然后在Player的脚本组件中将Layer设置为enemy和level,这样主角射击时其射线就可以击中敌人和场景模型。在Project窗口找到Prefabs/FX.Prefab将其作为射击时击中目标的特效,再找到rawdata/Sound Pack/shot.WAV将其作为射击的音效。</p><p><img src="https://img-blog.csdn.net/20160903164120604" alt="" /></p><p>7. 创建一个新的脚本AutoDestroy.cs,将其指定给射击特效FX.Prefab。这个脚本的作用是在一定时间后自动销毁游戏体。</p><pre class="csharp" name="code">using UnityEngine;

[AddComponentMenu("MyGame/AutoDestroy")]
public class AutoDestroy : MonoBehaviour
{
public float timer = 1.0f;

void Start()
{
Destroy(gameObject, timer);
}
}

8. 选择敌人,在Inspector窗口选择【Add Component】→【Physics】→【Capsule Collider】为其添加碰撞体,调整碰撞体的属性使其和敌人相对贴合。在这里我们不做精确的碰撞判断,比如判断击中部位是头或脚等。如果需要这么做,在建模时需要专门创建一个低面数的碰撞模型并将其作为骨骼的子物体导出,在Unity中关闭其显示,并设置为Mesh Colliier碰撞体。



9. 打开enemy.cs脚本,添加OnDamage方法处理敌人被射中的逻辑,当生命值为零的时候敌人进入死亡状态。

/// <summary>
/// 敌人被击中时触发的逻辑代码
/// </summary>
/// <param name="damage">伤害数值</param>
public void OnDamage(int damage)
{
life -= damage;
// 如果生命为0,进入死亡状态
if (life <= 0)
{
animator.SetBool("death", true);
}
}

10. 在Update方法中添加处理死亡状态的代码,当敌人的死亡动画播放完时,更新UI界面并销毁敌人。

// 死亡状态
if (info.fullPathHash == Animator.StringToHash("Base Layer.death")
&& !animator.IsInTransition(0))
{
// 判断死亡动画是否播放完成
if (info.normalizedTime >= 1.0f)
{
// 加分
GameManager.Instance.SetScore(100);
// 销毁自身
Destroy(gameObject);
}
}

11. 在Update方法中修改处理攻击状态的代码,当敌人攻击主角时,调用主角的OnDamage方法更新主角的生命值。

// 攻击状态
if (info.fullPathHash == Animator.StringToHash("Base Layer.attack")
&& !animator.IsInTransition(0))
{
animator.SetBool("attack", false);
// 面向主角
RotateTo();
// 如果动画播放完,重新进入待机状态
if (info.normalizedTime >= 1)
{
animator.SetBool("idle", true);
// 重置计时器
timer = 2;
// 更新主角生命值
player.OnDamage(1);
}
}

12. 运行游戏,如果主角距离敌人过近就会被敌人攻击而减少生命值。单击鼠标左键向敌人开火以消灭敌人并获得加分。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c# unity unity3d 游戏 脚本