上一篇博客完成了第一人称视角的角色控制,这一张在开始武器系统之前,我们先做一个用户游戏时的UI界面,方便后面对武器系统进行检验。
通过GameObject--》UI--》Canvas创建一个画布,在画布下添加以下元素:
其中bullet为子弹图标,aimer为准星图标,将相应的图片资源添加进去,然后调整好位置,即可。
现在开始写武器系统,首先在主摄像机下创建一个空物体,命名为WeaponManager,然后在这个空物体下创建三个空物体来记录三个武器的位置,再将各个武器的prefab放进这三个空物体。
之所以专门创建一个空物体来标记武器位置,是因为武器播放动画时会产生一定的位移,而位移的中心为其父物体(这里可能是动画的问题,没办法bake,可以bake的话应该就不用创建Gunpos等三个空物体了)。然后,每一个武器的prefab中包括三个元素:武器模型、一个空物体(标记枪口位置)、一个点光源(提升开火效果)。
为每个武器的prefab添加好Auidio Sourse和Animation(没有用Animator是因为动画太少,逻辑很简单[其实是懒]),现在终于可以开始写代码了。
首先是不同武器的脚本,下面示例一个手枪的(需要注意的是,这里我们的武器开火时并没有真的发射出子弹,仅是利用一种视觉错觉,弹道实际上是从摄像头发出的一条射线):
using UnityEngine;
using UnityEngine.UI;
public class gun : MonoBehaviour
{
public Transform m_camera;//主相机
public Transform textruehit;//击中墙壁的图片
public Transform spark;//击中物体的特效
public Transform flashfire;//开火特效
public Transform gunmouth;//枪口的位置
public Light m_light;//为了增强开火特效的点光源
public Text BulletText;//子弹数文本
private RaycastHit hit; // 命中的对象
[SerializeField]
private int curbulletnum;
[SerializeField]
private int maxbulletnum;
[SerializeField]
private int storebulletnum;
private float timer;//为了记录开火的时间间隔
[SerializeField]
private float AccuracyRange;//子弹的准度范围
private float reloadtimer;//记录换弹的时间间隔
[SerializeField]
private int damage;
[SerializeField]
private int shootrange;
public AudioClip ShootAudio;
public AudioClip ReloadedAudio;
private int m_animation;
public AnimationClip IdleAnimation;
public AnimationClip ReloadAnimation;
public AnimationClip ShootAnimation;
string IdleS;
string ReloadS;
string ShootS;
void Start()
{
IdleS = IdleAnimation.name;
ReloadS = ReloadAnimation.name;
ShootS = ShootAnimation.name;
m_light.transform.position = gunmouth.position;
m_light.range = 0;
m_animation = 0;
reloadtimer = 0;
timer = 0;
}
void Update()
{
SwicthAimation();
#region 换子弹、开火
//主动换弹
if (Input.GetKeyDown("r") && storebulletnum > 0 && curbulletnum != maxbulletnum && m_animation != 1)
{
m_animation = 1;
}
if (Input.GetButton("Attack") && m_animation == 0)
{
if (curbulletnum == 0 && storebulletnum > 0) //自动换弹
{
m_animation = 1;
}
else if (curbulletnum > 0)
{
ShootFire();
m_animation = 2;
}
}
#endregion
FreshBulletText();
}
//生成枪打到障碍物的特效
void PlayHitEffect(Vector3 hitpos, Vector3 hitdirection)
{
//获得一个垂直于墙壁向上的方向
Quaternion HitRotation = Quaternion.FromToRotation(Vector3.up, hit.normal);
GameObject s = Instantiate(spark.gameObject, hitpos, HitRotation);
GameObject h = Instantiate(textruehit.gameObject, hitpos, HitRotation);
Destroy(s, 1);
Destroy(h, 3);
}
void ShootFire()
{
// 播放枪发射子弹时枪口的火焰
GetComponent<AudioSource>().PlayOneShot(ShootAudio);
//确定弹道方向
Vector3 direction = m_camera.TransformDirection
(Vector3.forward + new Vector3
(Random.Range(-AccuracyRange, AccuracyRange), Random.Range(-AccuracyRange, AccuracyRange)));
// 播放枪发射子弹时枪口的火焰
GameObject f = Instantiate
(flashfire.gameObject, gunmouth.transform.position, gameObject.transform.rotation);
if (Physics.Raycast(m_camera.position, direction, out hit, shootrange))
{
Debug.Log(hit.collider.gameObject.name);
//这里的代码以后会进行改动,对于击中不同的物体会有不同的效果
PlayHitEffect(hit.point, direction);
}
Destroy(f, 0.04f);
curbulletnum--;
}
void Reload()
{
if (storebulletnum > maxbulletnum - curbulletnum)
{
storebulletnum -= maxbulletnum - curbulletnum;
curbulletnum = maxbulletnum;
}
else
{
curbulletnum += storebulletnum;
storebulletnum = 0;
}
}//装弹计算
void FreshBulletText()
{
BulletText.text = curbulletnum.ToString() + "/" + storebulletnum.ToString();
}
void SwicthAimation()//转换动画
{
if (m_animation == 0)
{
GetComponent<Animation>().CrossFade(IdleS);
}
if (m_animation == 1)
{
GetComponent<Animation>().CrossFade(ReloadS);
reloadtimer += Time.deltaTime;
}
if (m_animation == 2)
{
GetComponent<Animation>().Play(ShootS);
m_light.range = 3;
timer += Time.deltaTime;
}
if (reloadtimer >= GetComponent<Animation>()[ReloadS].length && m_animation == 1)// 当装弹动画结束时
{
Reload();
m_animation = 0;
reloadtimer = 0;
}
if (timer >= GetComponent<Animation>()[ShootS].length && m_animation == 2)// 当射击动画结束时
{
m_light.range = 0;
timer = 0;
m_animation = 0;
}
}
public int GetCurBulletNum()
{
return curbulletnum;
}
};
其他两个武器的脚本基本和手枪的差不多,就不放上来了,另外狙击枪需要再添加一个瞄准镜脚本:
扫描二维码关注公众号,回复:
2939725 查看本文章
![](/qrcode.jpg)
//瞄准镜脚本
using UnityEngine;
public class sniperscope : MonoBehaviour
{
[HideInInspector]
public bool IsAiming;
[SerializeField]
private float ZoomLevel;
[SerializeField]
private float ZoomInSpeed;
[SerializeField]
private float ZoomOutSpeed;
private float initFOV;//初始值
public Texture aimtexture;//瞄准时加载的图片
[HideInInspector]
public bool CanAiming;
void Start()
{
CanAiming = true;
initFOV = Camera.main.fieldOfView;
}
void Update()
{
//如果条件允许
if (gameObject&& CanAiming)
{
if (Input.GetMouseButton(1)) ZoomView();
else ZoomOut();
}
//如果正在瞄准,但当前条件不允许,回到正常状态
if (IsAiming && !CanAiming)
{
Camera.main.fieldOfView=initFOV;
IsAiming = false;
}
}
private void ZoomView()
{
if (Camera.main.fieldOfView - (Time.deltaTime * ZoomInSpeed) >= initFOV / ZoomLevel)
{
Camera.main.fieldOfView -= Time.deltaTime * ZoomInSpeed;
}
IsAiming = true;
}
private void ZoomOut()
{
if (Camera.main.fieldOfView + (Time.deltaTime * ZoomOutSpeed) <= initFOV)
{
Camera.main.fieldOfView += Time.deltaTime * ZoomOutSpeed;
}
IsAiming = false;
}
void OnGUI()
{
if (IsAiming)
{
Rect rect = new Rect(0,//矩形的X轴坐标
0,//矩形的y轴的坐标
Screen.width,//矩形的宽
Screen.height);//矩形的高
GUI.DrawTexture(rect, aimtexture);//绘制图形
}
}
}
处理好三个武器以后,再写一个武器管理脚本,主要对武器的切换进行管理,挂在命名为weaponmanager 的空物体上:
using UnityEngine;
public class weaponmanager : MonoBehaviour
{
public int CurrentWeapon;//现在的武器
public bool CanChangeWeapon;//是否可以更换武器
public GameObject weapon1;//weapon1 prefab
public GameObject weapon2;//weapon2 prefab
public GameObject weapon3;//weapon3 prefab
public gun m_gun; //手枪
public subgun m_subgun; //冲锋枪
public gun m_sniperrifle;// 狙击枪和手枪用的是同一个脚本,所以类型是一样的
public sniperscope m_sniperscope;//瞄准镜
void Start()
{
NullWeapons();
CurrentWeapon = 1;
weapon1.SetActive(true);//默认将武器1设置为可用
weapon1.GetComponent<Animation>().Play("Gun_On");
Cursor.visible = false;//隐藏鼠标
}
void Update()
{
if (!m_sniperscope.IsAiming)//当狙击枪没有瞄准时可以更换武器
CanChangeWeapon = true;
else CanChangeWeapon = false;
if (m_sniperrifle.GetCurBulletNum()==0)//当狙击无弹时不可瞄准
m_sniperscope.CanAiming = false;
else m_sniperscope.CanAiming = true;
#region 更换武器
//通过滑轮换武器
if ((Input.GetAxis("Mouse ScrollWheel") < 0) & CanChangeWeapon == true)
{
CurrentWeapon -= 1;
if (CurrentWeapon > 3) CurrentWeapon = 1;
if (CurrentWeapon < 1) CurrentWeapon = 3;
Switch();
}
if ((Input.GetAxis("Mouse ScrollWheel") > 0) & CanChangeWeapon == true)
{
CurrentWeapon += 1;
if(CurrentWeapon>3) CurrentWeapon = 1;
if(CurrentWeapon<1) CurrentWeapon = 3;
Switch();
}
//通过按键换武器
if (Input.GetKeyDown("1") & CanChangeWeapon == true)
{
if (!m_gun.isActiveAndEnabled)
{
CurrentWeapon = 1;
Switch();
}
}
if (Input.GetKeyDown("2") & CanChangeWeapon == true)
{
if (!m_subgun.isActiveAndEnabled)
{
CurrentWeapon = 2;
Switch();
}
}
if (Input.GetKeyDown("3") & CanChangeWeapon == true)
{
if (!m_sniperrifle.isActiveAndEnabled)
{
CurrentWeapon = 3;
Switch();
}
}
#endregion
}
public void NullWeapons()//隐藏所有武器
{
weapon1.SetActive(false);
weapon2.SetActive(false);
weapon3.SetActive(false);
}
public void Switch()// 转换武器
{
if (CurrentWeapon == 1)
{
NullWeapons();
weapon1.SetActive(true);//将武器1设置为可用
weapon1.GetComponent<Animation>().Play("Gun_On");
}
if (CurrentWeapon == 2)
{
NullWeapons();
weapon2.SetActive(true);//将武器2设置为可用
weapon2.GetComponent<Animation>().Play("SubGun_On");
}
if (CurrentWeapon == 3)
{
NullWeapons();
weapon3.SetActive(true);//将武器3设置为可用
weapon3.GetComponent<Animation>().Play("SniperRifle_On");
}
}
}
这样武器这边就基本弄好了,整体效果:
下一篇进行对敌人巡逻机器人的设计。