Unity3D通过射线检测实现子弹射击

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

 

关于射线检测实现子弹射击效果

  • 1、射线检测
  • 2、实现子弹发射的效果
  • 3、拓展(FPS游戏进场开始游戏选择)
    • (1)射线检测与组件LineRenderer的使用
    • (2)优化

一、射线检测是什么?

      射线检测是指通过发出一条射线,对射线碰撞到的碰撞体进行检测,发生碰撞满足条件时返回值为真。

其中包括:

1、Physics.Linecast 线性投射:开始位置到结束位置进行光线投射

2、Physics.Raycast(较常用):显示一条与碰撞器发生碰撞的射线,射线的方向和长度可以设置

3、Physics.RaycastAll:发出一条射线返回所有的碰撞,返回的是一个RaycastHit[]的结构体

4、Physics.OverlapSphere :球形射线检测,返回的是球形半径内的所有碰撞器collider[],可以用来判断捡拾物品以及手雷爆炸的触发等。

二、实现子弹射击效果

1.思路

        3D游戏场景中发射子弹,为了避免子弹穿透的问题,考虑通过射线检测,从枪口发出一条射线,在枪的射程范围内判断碰撞,发生碰撞证明击中物体,在发生碰撞的位置生成一个弹孔特效。

2.具体的实现

这里我以第一人称射击游戏(FPS)中实现的射击效果为例

代码如下(示例):

public class Player_OpenFire : MonoBehaviour
{
    //屏幕中心点位置坐标
    private Vector3 point;
    //FPS人物相机
    public Camera fpscam;    
    //枪的射程
    private float GunRange = 20f; 
    void Start()
    {   
        //第一人称相机在不停的转动,但是准星的位置始终是在屏幕的中心点,这里获取到屏幕的中心点位置坐标
        point = new Vector3(Screen.width / 2, Screen.height / 2, 0);
    }
    void Update()
    {
       OpenFire();
    }
     void OpenFire() 
    {
        RaycastHit hit;
        //初始化一条从屏幕中心点发出的射线
        Ray ray = fpscam.ScreenPointToRay(point);
        //这里的5表示的是所在的层,因为我的项目中用了多个相机,所以需要分层处理。(读者可以根据自己的需要自行修改)
        if (Physics.Raycast(ray, out hit, GunRange, 5))
        {
           //这里进行的判断是防止枪口朝向脚下时,射线碰撞到玩家,玩家身上产生弹孔,造成自己打自己的情况。
            if (hit.collider.gameObject.tag=="Player")
            {
                return;
            }
            //这里我的弹孔特效是做成了预设体,放在Resources目录下进行动态加载,参数hit.point表示的是发生碰撞的坐标点是一个Vector3值
            GameObject bullet = Instantiate(Resources.Load("Bullet/BulletMark1"), hit.point, Quaternion.identity); 
            //在Unity三维坐标中的一个点包括三个值(三角朝向、法线值、UV值),这里用到了碰撞点的法线值
            bullet.transform.LookAt(hit.point - hit.normal);
             //把子弹特效设为碰撞到的物体的子物体
            bullet.transform.parent = hit.collider.transform;
             //我的预设体方向做的时候有问题,所以这里加载后再次修改了方向(读者根据需要自行修改这行代码)
            bullet.transform.Rotate(90, 0, 0);
             //我们让子弹移动一点点位置,调整子弹的特效能够显示在碰撞的物体上,避免遮盖的现象
            bullet.transform.Translate(Vector3.back * 0.01f);
              //为了方便演示这里设置弹孔特效一秒后自动销毁(读者可以用子弹对象池进行修改)
            Destroy(bullet, 1f);                      
        }
    }

三、拓展(FPS游戏进场开始游戏选择)

(1)射线检测与组件LineRenderer的使用

脚本挂在给枪口的空物体使用,空物体要包含LineRenderer以及Outline组件Li,否则运行时会报错。

具体代码如下:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class StartGame : MonoBehaviour
{
    //获取LineRenderer组件
    private LineRenderer line;
    //获取开始游戏按钮组件
    public Button startGame;
    //获取初始射线的终点坐标
    Vector3 endPoint;
    // Start is called before the first frame update
    void Start()
    {
        line = this.GetComponent();
        endPoint = line.GetPosition(1);
        //给按钮添加点击事件
        startGame.onClick.AddListener(delegate() 
        {
            GameObject.Find("StartGame").SetActive(false);
        });
    }
    void Update()
    {
        //初始化一条射线,从枪口位置向正前方发射
        Ray ray = new Ray(this.transform.position, this.transform.forward);      
        RaycastHit hit;   
        //设置射线的碰撞距离为50米
        if (Physics.Raycast(ray, out hit, 50))
        {
            //判断是不是碰到了开始游戏按钮
            if (hit.collider.tag == "StartGame")
            {
                //碰到开始游戏按钮时使按钮出现明亮边框
                startGame.GetComponent().enabled = true;
                //绘制射线,颜色设置为红色
                Debug.DrawLine(transform.position, hit.point, Color.red);
                //将世界坐标转换为Local
                line.SetPosition(1, line.transform.InverseTransformPoint(hit.point));
                //点击了鼠标左键会执行回调函数
                if (Input.GetMouseButtonDown(0))
                {
                    /*
                     * 这里用伪代码简单说明可以实现的具体功能
                     * 1、可以实现上文提到的出现弹孔特效
                     * 2、可以跳转进入游戏界面
                     * 3、也可以通过异步加载的方式,进入加载界面,加载游戏(推荐使用这种方式设计,直接加载进入,如果游戏场景过大会造成卡顿,异步加载能够给用户更好的游戏体验)
                     */

                    ExecuteEvents.Execute(gameObject, new PointerEventData(EventSystem.current), ExecuteEvents.pointerClickHandler);
                }
                
            }
        }
        else
        {
            //没有碰到碰撞体时让按钮不能有明亮边框
            startGame.GetComponent().enabled = false;
            //同时恢复射线原有的样子
            line.SetPosition(1, endPoint);
        }    
    }
}

LineRenderer组件使用介绍:

   LineRenderer组件能够显示射线

Unity3D通过射线检测实现子弹射击_第1张图片

这里我没有使用World Space坐标是防止射线出现跑偏的现象造成的不真实效果。

LineRenderer中坐标点是可以编辑、添加的,同时要选中Loop使射线连续更加真实。

射线线条的宽度通过修改Width的值可以实现。默认值是1,我这里用0.01能够达到线条的效果而不是条状带。

线条的颜色是可以进行更改的,但是要注意的是必须要有材质才可以更改颜色,否则更改颜色是不起作用的。多个材质也只会有一个起作用。

Outline组件使用

Unity3D通过射线检测实现子弹射击_第2张图片

在这里使用时,默认不要勾选,当射线检测到时才显示明亮的边框。

EffectColer设置颜色,根据场景颜色基调以及视觉效果合理搭配。

EffectDistance设置宽高的大小,不同的值显示的边框的厚度不一样,值越大厚度越大。

Use Graphic Alpha:应用Alpha值的图像,视觉效果更好,同样的占的空间字节数也大。

(2)优化策略

上述代码中之所以设置endPoint这个点来存放最初的射线位置,是为了节省射线,避免穿透。

    line.SetPosition(1, line.transform.InverseTransformPoint(hit.point));

这句代码是将射线的终点设置为碰撞点,这样碰撞点之后就不会再出现射线,而离开碰撞物体后射线又能够通过endPoint点恢复原来的样子,不会产生固定长度的现象。

这里提到的一些问题都是出现的情况,读者可以自行尝试。

 

总结

      这里特别说明一点:通过3D人物场景触发Canvas上的Button按钮,属于与2D相结合进行射线检测,这里的处理方法是给按钮添加一个BoxCollider的碰撞器,才能够进行碰撞,检测射线。

你可能感兴趣的:(Unity3D,游戏开发,游戏,unity3d)