【总结】 射线射击-综合案例FPS 3d射击游戏制作

本文介绍了如何在Unity中使用脚本实现3D射击游戏中物体移动、旋转、摄像机跟随、射线追踪、怪物自动刷新和得分功能的开发过程。
该文章已生成可运行项目,

综合案例FPS3d射击游戏制作
1.实现控制物体移动旋转方向
  •WS控制物体前后左右移动
   •人物跟随随着鼠标可以左右上下旋转视角
2.实现摄像机第三人称视角跟踪
3.实现向屏幕中间发射射线(播放子弹特效、声音)
4.实现射线射中物体销毁敌人
5.实现自动刷怪物(间隔20),自动销毁怪物(60秒)
6.实现射中物体加分
7.添加射击音乐和特效

脚本1 现控制同一个物体上下左右移动

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ControlMove : MonoBehaviour
{
    //用户按下WASD控制物体往前后左右移动
    //用户左右滑动鼠标可以让主角左右旋转,上下滑动也一样

    public float MoveSpeed = 10f;

    public float RotateSpeed = 100f;

    float RotationY;//用来累积存储用户每次移动鼠标得出的值
    float RotationX;//用来累积存储用户每次移动鼠标得出的值


    void Update()
    {

     float MoveLeftR = Input.GetAxis("Horizontal");//当用户按下AD返回1-1
     float MoveFB = Input.GetAxis("Vertical");
     this.transform.Translate(new Vector3(MoveLeftR * 1, 0, 1 * MoveFB) * MoveSpeed * Time.deltaTime);

        float RotetY = Input.GetAxis("Mouse X");   
        float RotetX = Input.GetAxis("Mouse Y");        

         RotationY += RotetY* RotateSpeed * Time.deltaTime; //用来累积存储用户每次水平移动鼠标得出的值乘以旋转速度
         RotationX += -RotetX * RotateSpeed * Time.deltaTime;//用来累积存储用户每次上下移动鼠标得出的值乘以旋转速度
        
        this.transform.rotation = Quaternion.Euler(RotationX , RotationY, 0);
        LimitRotateX();
    }

    /// <summary>
    /// 限制上下旋转的最大角度和单次旋转角度
    /// </summary>

    void LimitRotateX()
    {
        #region
        //  if (RotationX > 360)
        //  {
        //      RotationX -= 360;
        //  }
        //  if (RotationX < -360)
        //  {
        //      RotationX += 360;
        //}
        #endregion
        RotationX = Mathf.Clamp(RotationX, -30f,30f);
    }   

}//end class

但是上面这个脚本不适合有身体的第一人称视角,因为人体角色左右旋转头部是可以跟随身体的,但是视角上下旋转是依靠头部单独旋转而不是身体上下仰翻,因此下面这个脚本把身体旋转和头部旋转分开了!

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;

public class PlayerController : MonoBehaviour
{

    //1.WS控制物体前后移动
    //2.人物跟随随着鼠标可以左右上下旋转视角
    public Transform Player;//主角
    public float movespeed = 100f;
    public float Upspeed = 100f;


    public float Rotatespeed = 1000f;
    Transform PlayerEyes;

    float RotateX = 0f;
    float RotateY = 0f;

    private void Start()
    {
        PlayerEyes = Player.GetChild(0);//获取主角身上的摄像机,摄像机必须在第一个子物体位置

        UnityEngine.Cursor.lockState = CursorLockMode.Locked;
    }
    void Update()
    {
        //下面的代码实现 使用Unity 的输入轴来实现前后移动--------------

        float MoveZ = Input.GetAxis("Vertical");
        float UpValue= Input.GetAxis("Jump");
        Player.Translate(new Vector3(0, UpValue* Upspeed*Time.deltaTime, MoveZ * movespeed * Time.deltaTime));

        ViewRotation();



    }

    void ViewRotation()
    {
        //下面的代码实现【主角】左右视角旋转------------------

        float MouseRotateY = Input.GetAxis("Mouse X");
        Player.Rotate(new Vector3(0, MouseRotateY, 0));
        RotateY += MouseRotateY * Rotatespeed * Time.deltaTime;
      

        //下面的代码实现了【头部】上下视角旋转----------------------

        float MouseRotateX = Input.GetAxis("Mouse Y");

        RotateX += -1 * MouseRotateX * Rotatespeed * Time.deltaTime;//从鼠标轴获取鼠标滑动的值并且做累加       

        RotateX = Mathf.Clamp(RotateX, -35, 35);
        PlayerEyes.localRotation = Quaternion.Euler(RotateX, 0, 0);
    }


}

脚本2 第三人称 摄像机跟踪主角的脚本(本案例是第一人称,暂时不用)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FollowOBJ : MonoBehaviour
{
    //业务逻辑:这个脚本用来控制摄像机跟踪一个目标
    /*程序逻辑: 1.设置好一个被跟踪的目标 2.我让摄像机的坐标和目标保持同步 3.摄像机得看向目标点  */

    GameObject FollTarget;
    GameObject Looktarget;
    Vector3 speed = Vector3.zero;
    public float FollowTime = 10f;
    void Start()
    {
        FollTarget = GameObject.Find("CameraTarget");//拿到目标点
        Looktarget= GameObject.Find("GameObject");//拿到目标点
    }

    // Update is called once per frame
    void Update()
    {
        this.transform.position = Vector3.SmoothDamp(this.transform.position, FollTarget.transform.position, ref speed, FollowTime);
        
        this.transform.LookAt(Looktarget.transform);

    }
}//end class

脚本3  控制射击 向屏幕中间的瞄准器位置射击

2025 版本:


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RayShotManager : MonoBehaviour
{
    //射线,从一个点出发朝向一个方向无限延伸

    //1.创建一根射线(起点和方向)  2.投射出去 3.获取射线击中的对象
    
    public Camera PlayerCamera;
    public AudioSource PlayerAudio;
    public AudioClip ShotAudio;
    public AudioClip BGMtAudio;

    private void Start()
    {
        PlayerAudio.Play();
    }

    void Update()
    {
        //oneRay = new Ray(one.transform.position, one.transform.forward);

        Ray oneRay = PlayerCamera.ScreenPointToRay(new Vector3(Screen.width / 2, Screen.height / 2, 1f));
        //以某个相机为起点,以起点和屏幕上的某个点的连线为方向,创建一根射线  
        if (Input.GetMouseButtonDown(0))
        {
            RaycastHit RayCastHitinfromation;
            if (Physics.Raycast(oneRay, out RayCastHitinfromation))
            {
                Debug.Log("发射,播放音效");//调用播放音效函数
                PlayerAudio.PlayOneShot(ShotAudio);
                
                Debug.Log("射线击中了对象,并且我给你把对象信息存到了,你给我的RayCastHitinfromation,他的名字叫:" + RayCastHitinfromation.transform.gameObject);
                GameObject tempObj = RayCastHitinfromation.transform.gameObject;
                if (tempObj != null&& tempObj.tag=="Monster")
                { 
                Destroy(tempObj);
                    Debug.Log("恭喜你击中了目标!加100分!");//调用加分函数
                }
                
            }

        }
     
        
    }
}

添加音效和特效播放:

using System.Collections;
using System.Collections.Generic;
using Unity.Android.Gradle;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.ParticleSystemJobs;

public class ShotManager : MonoBehaviour
{
    // 【1】按下鼠标左键 【2】发射射线,创建射线(摄像机为起点-往摄像机前方发射),投射射线 【3】击中物体销毁物体
    
    // 玩家摄像机引用,需要在Unity编辑器中拖拽摄像机到这个字段
    public Camera PlayerCamera;

    [Header("粒子特效设置")]
    // 击中物体时播放的粒子特效,在Unity编辑器中拖入一个粒子特效预制体
    public ParticleSystem hitParticleEffect;  // 击中时的粒子特效,从仓库中拖一个特效预制体进去就可以了 

    [Header("射击声音设置")]
    // 射击时播放的声音文件
    public AudioClip shotsound;
    // 播放声音的音频源组件
    public AudioSource AudioPlay;
    
    // 每一帧都会调用的函数
    void Update()
    {
        // 【1】检测是否按下鼠标左键(0代表左键,1代表右键,2代表中键)
        if (Input.GetMouseButtonDown(0))
        {
            // 临时变量,用于存储创建的粒子特效实例
            ParticleSystem tempPart;
            
            // 【2】创建射线:从屏幕正中心发射一条射线
            // Screen.width/2 和 Screen.height/2 表示屏幕中心点
            // 第三个参数10f是射线起点距离摄像机的深度,一般保持默认即可
            Ray ShotRay = PlayerCamera.ScreenPointToRay(new Vector3( Screen.width/2, Screen.height/2, 10f));
          
            // 存储射线击中物体的信息
            RaycastHit Hitinfo;
            
            // 【2】投射射线并检测是否击中物体
            // Physics.Raycast 会返回true如果射线击中了碰撞体
            if (Physics.Raycast(ShotRay, out Hitinfo))
            {
                // 【4】检查击中的物体是否带有"Monster"标签
                if (Hitinfo.collider.gameObject.tag=="Monster")
                {
                   // 【3】销毁被击中的怪物物体
                   Destroy(Hitinfo.collider.gameObject);
                   // 在控制台输出销毁信息,方便调试
                   Debug.Log("销毁了物体"+ Hitinfo.collider.gameObject.name);
                    
                   // 在击中点创建粒子特效
                   // Hitinfo.point 是射线与物体碰撞的精确位置
                   // Quaternion.LookRotation(Hitinfo.normal) 让特效朝向碰撞表面的法线方向
                   tempPart = GameObject.Instantiate(hitParticleEffect, Hitinfo.point,Quaternion.LookRotation(Hitinfo.normal));
                   
                   // 播放粒子特效
                   tempPart.Play();
                   
                           
                   
                   // 在粒子特效播放完成后自动销毁特效对象
                   // tempPart.main.duration 获取粒子系统的主持续时间
                   Destroy(tempPart.gameObject,tempPart.main.duration);
                   
                   // 在控制台输出提示信息
                   Debug.Log("销毁");

                   // 播放射击音效
                   AudioPlay.PlayOneShot(shotsound);
                }
            }
            // 注意:如果射线没有击中任何物体,或者击中的不是怪物,就不会执行任何操作
        }
    }
}

2024 版本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RayShotScreMid : MonoBehaviour
{
    // 射线发射到屏幕中间,击中物体,销毁物体
    Camera MaCamera;
    Vector3 ScreenMidPos;
    void Start()
    {
        MaCamera = Camera.main;
        ScreenMidPos = new Vector3(Screen.width / 2, Screen.height / 2, 10f);  // 初始化屏幕中央的点的坐标
        Debug.Log(Screen.width);
    }

    // Update is called once per frame
    void Update()
    {
        Ray OneRay = Camera.main.ScreenPointToRay(ScreenMidPos);//把屏幕中间的点转换成射线,摄像机为射线起点
        if (Input.GetMouseButtonDown(0))
        {
            RaycastHit ShotEnemy;
            if (Physics.Raycast(OneRay, out ShotEnemy))
            {
                Debug.Log("射中了物体他叫" + ShotEnemy.transform.name);
                Destroy(ShotEnemy.transform.gameObject);//销毁射中的物体
            }
        }   
    }
}

脚本4 随机生成怪物

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CreateMonster : MonoBehaviour
{
    // 这个脚本是间隔10秒时间,克隆怪物,到随机坐标
    //两个方案1.时间计时器 2.协程

    float JshiqiTime = 0f;
    GameObject MonsterPrefab;//克隆模板
    float ClonePosX;//随机克隆坐标的X数值
    float ClonePosZ;//随机克隆坐标的Y数值

    Transform CloneMidPos;

    void Start()
    {
        MonsterPrefab = GameObject.Find("Enemy");//拿到克隆模板
        CloneMidPos = GameObject.Find("Monsterpos").transform;
    }

    // Update is called once per frame
    void Update()
    {
       
        JshiqiTime += Time.deltaTime;//开始记录游戏运行的时间
        Debug.Log(JshiqiTime / 1);
        if (JshiqiTime >= 3f)
        {
            Debug.Log(JshiqiTime);
            Debug.Log("10秒了" + JshiqiTime);
            JshiqiTime = 0;
            cloneMonster();//开始克隆
        }
    }

    void cloneMonster()
    {
        ClonePosX = Random.Range(5f, 50f) + CloneMidPos.position.x;
        ClonePosZ = Random.Range(3f, 30f) + CloneMidPos.position.y;
        for (int i = 0; i < 10; i++) //克隆10个以CloneMidPos为中心方圆10米的随机坐标
        {
            GameObject tempobj=  GameObject.Instantiate(MonsterPrefab, new Vector3(ClonePosX, 0, ClonePosZ), Quaternion.identity);
            Debug.Log("10秒了开始克隆" );
            Destroy(tempobj, 3f);
        }
    }
}

本文章已经生成可运行项目
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Unity青子

难题的解决使成本节约,求打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值