
综合案例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);
}
}
}

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

被折叠的 条评论
为什么被折叠?



