PICO VR 《制作一把能量手枪》

演示视频地址:

先放gif,等我有时间在bilibili出讲解视频

能量球shader参考文档:

Shader 能量法球特效_unity 用纹理实现球型能量能量进度效果-CSDN博客

开发流程(面向对象):

1.交互方式,常见的就是,手柄扳机键长按蓄能,随着时间,能量球放大到一定大小,停止放大,松开扳机键发射能量弹。

2.确定能量球的特效效果,碰撞特效,音效。

3.因为蓄能发射的关系,所以代码实现子弹单次发射的逻辑就好,常用的对象池实现方式,碰撞到物体后实例化碰撞的粒子特效。碰撞后是否销毁物体,还是让物体失活。

4.用协程改变能量球大小

下面是hierarchy的物体关系图

没啥注意的,记得吧子弹预制体里的刚体设置为,CxxxxDynamic,碰撞有关的物体记得加上刚体和碰撞体(两者都要)。

下面贴出关键代码:

1.子弹预制体实例化(对象池)的代码:

 public AudioSource audio;
 private GameObject bj1;
 private GameObject bj2;
 private GameObject bj3;
 private GameObject bj4;
 private ZHZUtil util;
 public static BulletIncubator instance;
 public int amountToPool;
 public GameObject m_bullet;
 public List<GameObject> m_pooledObjectlist;
 private void Awake()
 {
     instance = this;

 }
 void Start()
 {
     GameObject[] siblings = FindObjectsOfType<GameObject>();
     foreach (GameObject child in siblings)
     {
         if (child.name == "部件1")
         {
             bj1 = child;
         }
         if (child.name == "部件2")
         {
             bj2 = child;
         }
         if (child.name == "部件3")
         {
             bj3 = child;
         }
         if (child.name == "部件4")
         {
             bj4 = child;
         }
     }
     //实例化子弹
     m_pooledObjectlist = new List<GameObject>();
     GameObject tmp;
     for (int i = 0; i < amountToPool; i++)
     {
         tmp = Instantiate(m_bullet, transform);
         tmp.SetActive(false);
         m_pooledObjectlist.Add(tmp);
     }
 }
 /// <summary>
 /// 获取可用的子弹
 /// </summary>
 /// <returns></returns>
 public GameObject GetPooledObject()
 {
     if (m_pooledObjectlist.Count != 0)
     {
         for (int i = 0; i < m_pooledObjectlist.Count; i++)
         {
             if (!m_pooledObjectlist[i].activeInHierarchy)
             {
                 GameObject zidan = m_pooledObjectlist[i];
                 m_pooledObjectlist.RemoveAt(i);
                 return zidan;

             }

         }
     }
     else
     {
         if (bj1.active || bj2.active || bj3.active || bj4.active)
         {
             var newpop = ZHZVRKit.PopUp("很抱歉你挑战失败了,还是去学习吧!", duration: 99f);
             Debug.Log("结束");
             audio.Play();
             Invoke("goanohthoerscene", 5f);
             return null;
         }
     }

     return null;

 }
 public void goanohthoerscene()
 {
     SceneManager.LoadScene(1);
 }

2.子弹生成与发射时的逻辑代码:

 [Header("发射子弹")]
 private GameObject go;
 public float bulletspeed;
 public XRController rController;
 private BulletIncubator m_BulletIncubator;
 private ZHZUtil util;
 private float shootingtime = 0.5f;
 private bool isfire = false;
 [Header("改变子弹的大小")]
 private float targetSize = 10f; // 目标大小
 public float changeDuration = 2f; // 变化持续时间

 private Vector3 initialSize; // 初始大小
 private Vector3 targetScale; // 目标缩放值
 private float elapsedTime = 0f; // 已过去的时间
 private bool isbuilt = true;
 [Header("音效")]
 public AudioSource juneng;
 public AudioSource fashe;
 private Coroutine changesize_zidan=null;
 private float t=0;
 //public Texture2D MyCursor;
 // Start is called before the first frame update
 private void Awake()
 {
     util = ZHZUtil.Instance;
     util.ResetInstance();
 }
 void Start()
 {

    
     m_BulletIncubator = BulletIncubator.instance;
    
 }

 // Update is called once per frame

 void Update()
 {

     /* if (Input.GetMouseButton(0))
      {
          elapsedTime += Time.deltaTime;
          Debug.Log("built");
          built_zi_dan();
          Debug.Log(t);
      }
      if (Input.GetMouseButtonUp(0) && isfire)
      {

          go.transform.parent = null;
          StopCoroutine(change_zidan_size());


          isbuilt = true;

          biubiu();
      }*/



     if (rController.inputDevice.TryGetFeatureValue(CommonUsages.trigger, out float trigger) && trigger > 0.5f)
     {
         elapsedTime += Time.deltaTime;

         built_zi_dan();


     }
     if (rController.inputDevice.TryGetFeatureValue(CommonUsages.trigger, out float trigger1) && trigger1 == 0f && isfire)
     {
         elapsedTime = 0f;
         go.transform.parent = null;
         StopCoroutine(change_zidan_size());


         isbuilt = true;

         biubiu();

     }

 }

 private void biubiu()
 {
     Ray ray = new Ray(rController.transform.position, rController.transform.forward);
     if (Physics.Raycast(ray, out RaycastHit hit, Mathf.Infinity) && isfire)
     {


        
         juneng.Stop();
         fashe.Play();
         Debug.Log("biu");

         go.GetComponent<Rigidbody>().velocity = rController.transform.forward * bulletspeed;
         
         isfire = false;
       isbuilt = true;
        

     }
 }
 private void built_zi_dan() {
     /*if (rController.inputDevice.TryGetFeatureValue(CommonUsages.trigger, out float trigger)){*/
     if (isbuilt)
     {
         elapsedTime = 0f;
         t = 0f;
        
         go = m_BulletIncubator.GetPooledObject();
         isbuilt = false;
         if (go != null)
         {
             juneng.Play();
             go.transform.position = transform.Find("gGUN03h").Find("startpos").position;
             go.SetActive(true);
             go.transform.parent = rController.transform;
             initialSize = go.transform.localScale;
             targetScale = initialSize * targetSize;


             changesize_zidan = StartCoroutine(change_zidan_size());

             isfire = true;
         }
         
     }
        
   
     /*}*/
 }
 private IEnumerator change_zidan_size() {
    
     while (elapsedTime < changeDuration)
     {
         Debug.Log("t=" + t);
          t = elapsedTime / changeDuration;
         if (go != null)
         {
             go.transform.localScale = Vector3.Lerp(initialSize, targetScale, t);
         }
         yield return null;
     }
     if (go != null)
     {
         go.transform.localScale = targetScale;
     }
 }

木箱往返运动代码:

    /// <summary>
    /// 物体和方位信息
    /// 各个部件,在爆炸前和爆炸后的位置和方向信息
    /// </summary>
    [Serializable]
    public class goPositions
    {
        [Header("物体")]
        [SerializeField]
        public GameObject go;

        [Header("爆炸前的位置")]
        [SerializeField]
        public Vector3 positionStart;

        [Header("爆炸前的方向")]
        [SerializeField]
        public Quaternion rotationStart;

        [Header("爆炸后的位置")]
        [SerializeField]
        public Vector3 positionEnd;

        [Header("爆炸后的方向")]
        [SerializeField]
        public Quaternion rotationEnd;
    }

    [Header("部件的根物体")]
    [SerializeField]
    public GameObject root;

    [Header("部件以及方位信息列表")]
    [SerializeField]
    public List<goPositions> GoPositions = new List<goPositions>();

    [Header("散开的时间")]
    [SerializeField]
    public float time;

    /// <summary>
    /// pingpong效果的进度条值
    /// </summary>
    private float stepValue;

    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
       

        //更新物体的位置和方向——pingpong效果
        stepValue = Mathf.PingPong(Time.time, time) / time;
        GoPositions.ForEach(x =>
        {
            x.go.transform.position = Vector3.Lerp(x.positionStart, x.positionEnd, stepValue);
            x.go.transform.rotation = Quaternion.Lerp(x.rotationStart, x.rotationEnd, stepValue);
        });
    }

    /// <summary>
    /// 物体摆放到初始位置
    /// </summary>
    void SetToStartTransform()
    {
        GoPositions.ForEach(
            x =>
            {
                x.go.transform.position = x.positionStart;
                x.go.transform.rotation = x.rotationStart;
            });
    }

    void SetToEndTransform()
    {
        GoPositions.ForEach(
            x =>
            {
                x.go.transform.position = x.positionEnd;
                x.go.transform.rotation = x.rotationEnd;
            });
    }


#if UNITY_EDITOR // Editor的非running状态使用
    [ContextMenu("记录【部件和开始方位】")]
#endif
    public void InitTransformStart()
    {
        GoPositions.Clear();

        root.transform.GetComponentInChildren<Transform>().Cast<Transform>().ToList()
            .ForEach(item =>
            {
                
                if (item.name.Contains("部件")) //
                {
                    Debug.Log(item.name);
                    goPositions gp = new goPositions();
                    gp.go = item.gameObject;
                    gp.positionStart = item.gameObject.transform.position;
                    gp.rotationStart = item.gameObject.transform.rotation;
                    GoPositions.Add(gp);
                }
                else
                {
                    //
                }
            });
        Debug.Log("初始方位记录完毕");
    }


#if UNITY_EDITOR // Editor的非running状态使用
    [ContextMenu("记录【结束方位】")]
#endif
    public void InitTransformEnd()
    {
        var allGos = root.transform.GetComponentInChildren<Transform>().Cast<Transform>().ToList();

        GoPositions.ForEach(x =>
        {
            x.positionEnd = allGos.Where(go => go.gameObject.name == x.go.name).ToList()[0].position;
            x.rotationEnd = allGos.Where(go => go.gameObject.name == x.go.name).ToList()[0].rotation;

        });
        Debug.Log("结束方位记录完毕");
    }

子弹本身的逻辑代码:

  public ParticleSystem boompt;
  
  private GameObject bj1;
  private GameObject bj2;
  private GameObject bj3;
  private GameObject bj4;
  private static int count=0;
  void Start()
  {
      GameObject[] siblings = FindObjectsOfType<GameObject>();
      foreach (GameObject child in siblings)
      {
          if (child.name == "部件1")
          {
            bj1 = child;
          }
          if (child.name == "部件2")
          {
              bj2 = child;
          }
          if (child.name == "部件3")
          {
              bj3 = child;
          }
          if (child.name == "部件4")
          {
              bj4 = child;
          }
      }
     
  }

  // Update is called once per frame
  void Update()
  {
    
  }
 
  private void OnTriggerEnter(Collider other)
  {

      Rigidbody rb = other.GetComponent<Rigidbody>();
      if (rb != null)
      {
          Debug.Log(other.name);
          // 获取碰撞位置
          Vector3 collisionPos = other.ClosestPoint(transform.position);

          // 实例化爆炸特效
          Instantiate(boompt, collisionPos, Quaternion.identity);
          if (other.name == "部件1") {bj1.SetActive(false);count+=1;}
          if (other.name == "部件2") {bj2.SetActive(false);count+= 1; }
          Debug.Log(count);

          if (other.name == "部件3"){ bj3.SetActive(false);count+=1; }
          if (other.name == "部件4") {bj4.SetActive(false);count+=1 ; }

          if (count == 4)
          {
              SceneManager.LoadScene(1);
          }
          Destroy(transform.gameObject);
          Debug.Log("打爆了" + count);
          
      }
     
  }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值