unity制作手游fps僵尸游戏


介绍

在这里插入图片描述

在这里插入图片描述
利用协程、枚举、动画器、导航等知识点。
实现移动、切枪、换弹、射击、僵尸追踪、攻击。


制作基本UI

制作人类血条、僵尸血条、移动按钮、换弹按钮、射击按钮、切枪按钮、准星。

在这里插入图片描述


枚举控制角色移动

在这里插入图片描述
Move(Movestate moveState): 根据传入的移动状态,控制玩家的移动。可以根据Movestate枚举类型的值来选择不同的移动方向。
StopMove(): 停止玩家的移动,将移动状态设置为停止状态。
MoveForward(): 将移动状态设置为向前移动状态。
MoveBackward(): 将移动状态设置为向后移动状态。
MoveLeft(): 将移动状态设置为向左移动状态。
MoveRight(): 将移动状态设置为向右移动状态。
Button_Move(string state): 通过传入的字符串参数设置移动状态。使用System.Enum.Parse将字符串解析为Movestate枚举类型的值。
Button_Drag(): 处理鼠标拖动的操作,获取鼠标在水平和垂直方向上的移动量,调用Rotatingplay()和RotatingCamera()函数来旋转玩家和相机。
Rotatingplay(float x): 绕Y轴旋转玩家。
RotatingCamera(float y): 绕X轴旋转相机,但限制了相机的旋转角度,确保相机不会旋转超过一定角度。
OnTriggerEnter(Collider other): 当触发器与其他物体发生碰撞时调用,输出调试信息。

using System.Collections; // 引用命名空间 System.Collections
using System.Collections.Generic; // 引用命名空间 System.Collections.Generic
using UnityEngine; // 引用命名空间 UnityEngine

public class player : MonoBehaviour // 定义一个名为 player 的类,并继承自 MonoBehaviour 类
{
    
    
	public enum Movestate{
    
    stop,forward,backward,left,right} // 定义一个名为 Movestate 的枚举类型,表示角色的移动状态

	public Movestate currentState; // 定义一个名为 currentState 的变量,表示当前角色的移动状态
	public float movespeed=1; // 定义一个名为 movespeed 的变量,表示角色的移动速度
	public CharacterController ch; // 定义一个名为 ch 的 CharacterController 变量,用于控制角色移动
	public Transform camtrans; // 定义一个名为 camtrans 的 Transform 变量,用于控制相机旋转

    void Start() // Start 方法,在第一帧开始前执行
    {
    
    
	    ch=GetComponent<CharacterController>(); // 获取角色的 CharacterController 组件,并赋值给 ch 变量
	    camtrans=transform.Find("Main Camera"); // 查找名为 "Main Camera" 的子对象,并将其 Transform 组件赋值给 camtrans 变量
    }

    void Update() // Update 方法,在每一帧更新时执行
    {
    
    
	    Move(currentState); // 调用 Move 方法,传入 currentState 参数
    }
	    
	public void Move(Movestate moveState){
    
     // 定义一个名为 Move 的公共方法,用于控制角色的移动
		
		switch(moveState){
    
     // 根据 moveState 参数的不同,执行不同的操作
		case Movestate.stop: // 如果 moveState 等于 stop,则执行下面的代码
			break; // 跳出 switch 语句
		case Movestate.forward: // 如果 moveState 等于 forward,则执行下面的代码
			//transform.Translate(transform.forward*Time.deltaTime*movespeed); // 使用 Transform 的 Translate 方法,向前移动角色
			ch.SimpleMove(transform.forward*movespeed); // 使用 CharacterController 的 SimpleMove 方法,向前移动角色
			break; // 跳出 switch 语句
		case Movestate.backward: // 如果 moveState 等于 backward,则执行下面的代码
			//transform.Translate(-transform.forward*Time.deltaTime*movespeed); // 使用 Transform 的 Translate 方法,向后移动角色
			ch.SimpleMove(-transform.forward*movespeed); // 使用 CharacterController 的 SimpleMove 方法,向后移动角色
			break; // 跳出 switch 语句
		case Movestate.left: // 如果 moveState 等于 left,则执行下面的代码
			//transform.Translate(-transform.right*Time.deltaTime*movespeed); // 使用 Transform 的 Translate 方法,向左移动角色
			ch.SimpleMove(-transform.right*movespeed); // 使用 CharacterController 的 SimpleMove 方法,向左移动角色
			break; // 跳出 switch 语句
		case Movestate.right: // 如果 moveState 等于 right,则执行下面的代码
			//transform.Translate(transform.right*Time.deltaTime*movespeed); // 使用 Transform 的 Translate 方法,向右移动角色
			ch.SimpleMove(transform.right*movespeed); // 使用 CharacterController 的 SimpleMove 方法,向右移动角色
			break; // 跳出 switch 语句
		default: // 如果 moveState 不等于以上任何一个值,则执行下面的代码
			break; // 跳出 switch 语句
		}
	}
	
	public void StopMove(){
    
     // 定义一个名为 StopMove 的公共方法,用于停止角色的移动
		
		currentState=Movestate.stop; // 将 currentState 变量的值设为 stop
	}
	
	public void MoveForward(){
    
     // 定义一个名为 MoveForward 的公共方法,用于向前移动角色
		
		currentState=Movestate.forward; // 将 currentState 变量的值设为 forward
	}
	
	public void MoveBackward(){
    
     // 定义一个名为 MoveBackward 的公共方法,用于向后移动角色
		
		currentState=Movestate.backward; // 将 currentState 变量的值设为 backward
	}
	
	public void MoveLeft(){
    
     // 定义一个名为 MoveLeft 的公共方法,用于向左移动角色
		
		currentState=Movestate.left; // 将 currentState 变量的值设为 left
	}
	
	public void MoveRight(){
    
     // 定义一个名为 MoveRight 的公共方法,用于向右移动角色
		
		currentState=Movestate.right; // 将 currentState 变量的值设为 right
	}
	
	public void Button_Move(string state){
    
     // 定义一个名为 Button_Move 的公共方法,用于响应移动按钮的点击事件,并根据点击事件的参数改变角色的移动状态
		
		var e=System.Enum.Parse(typeof(player.Movestate),state); // 将字符串类型的 state 参数转换为 Movestate 枚举类型,并赋值给 e 变量
		currentState=(player.Movestate)e; // 将 e 变量的值赋给 currentState 变量,从而改变角色的移动状态
	}
	
	public void Button_Drag(){
    
     // 定义一个名为 Button_Drag 的公共方法,用于响应拖拽事件
		
		float x=Input.GetAxis("Mouse X"); // 获取鼠标水平方向的移动距离
		float y=Input.GetAxis("Mouse Y"); // 获取鼠标垂直方向的移动距离
		Rotatingplay(x); // 调用 Rotatingplay 方法,传入 x 参数,用于旋转角色
		RotatingCamera(y); // 调用 RotatingCamera 方法,传入 y 参数,用于旋转相机
	}
	
	private void Rotatingplay(float x){
    
     // 定义一个名为 Rotatingplay 的私有方法,用于旋转角色
		
		transform.Rotate(0,x,0); // 使用 Transform 的 Rotate 方法,绕 y 轴旋转角色
	}
	
	private void RotatingCamera(float y){
    
     // 定义一个名为 RotatingCamera 的私有方法,用于旋转相机
		
		if ((-45< camtrans.rotation.x)&&(camtrans.rotation.x<45)) // 如果相机的 x 轴旋转角度在 -45 度到 45 度之间
		{
    
    
			camtrans.Rotate(-y,0,0); // 使用 Transform 的 Rotate 方法,绕 x 轴旋转相机
		}
	}
	
	private void OnTriggerEnter(Collider other){
    
     // 定义一个名为 OnTriggerEnter 的私有方法,用于响应角色进入触发器的事件,并输出调试信息
		
		Debug.Log("bang"); // 在控制台输出 "bang" 字符串
	}
}

切枪、设置音效、设置子弹威力、设置子弹时间间隔、换弹

在这里插入图片描述

Button_select():选择不同的枪械。
Button_Fire():开火,根据当前使用的枪械播放对应的音效,并调用castray()方法检测是否打中了僵尸。
pistolfire():手枪的开火逻辑。
tommyfire():冲锋枪的开火逻辑。
riflefire():步枪的开火逻辑。
Button_stopfire():停止持续射击(只有冲锋枪有此功能)。
showbulletsinfo():显示当前使用的枪械的子弹数量。
showtotalbullets():显示所有枪械的总弹药数量。
Canfire():设置canfire变量为true,表示可以开火。
Button_Reload():重新装填当前使用的枪械。
Reload():重新装填指定的枪械。

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

// 定义Start1类,继承MonoBehaviour类
public class Start1 : MonoBehaviour
{
    
    
// 声明枚举类型Guntype,包含pistol、tommy、rifle三种枪支类型
public enum Guntype{
    
    pistol, tommy, rifle}


// 当前使用的枪支类型
public Guntype usinggun;

// 枪支声音源和声音片段数组
public AudioSource gunsource;
public AudioClip[ ] gunclip;

// 是否可以开火的标志位、不同枪支的冷却时间、当前使用的枪支弹药数、弹匣容量、总弹药数和枪支伤害值
public bool canfire = true;
public float []cooltime;
public int[] usinggunbulets;
public int[] clapbullets;
public int[] totalbullets;
public float[] gundamage;

// 显示弹药数的文本和总弹药数文本数组、血液特效预制体
public Text showbullets;
public Text[] totalbulletstext;
public GameObject bloodzone;

// Start方法,初始化当前使用的枪支弹药数
private void Start(){
    
    
    usinggunbulets[0] = clapbullets[0];
    usinggunbulets[1] = clapbullets[1];
    usinggunbulets[2] = clapbullets[2];

    // 显示总弹药数
    showtotalbullets();
}

// Update方法,每帧执行一次
// ...

// 开始游戏按钮响应函数
public void startgame()
{
    
    
    // 切换到游戏场景
    SceneManager.LoadScene("游戏场景");
}

// 退出游戏按钮响应函数
public void exitgame(){
    
    
    // 退出应用程序
    Application.Quit();
}

// 开火按钮响应函数
public void Button_Fire(){
    
    
    // 根据当前使用的枪支类型进行开火操作
    switch (usinggun){
    
    
        case Guntype.pistol:
            pistolfire();
            break;
        case Guntype.tommy:
            tommyfire();
            break;
        case Guntype.rifle:
            riflefire();
            break;
    }
}

// 手枪开火函数
private void pistolfire(){
    
    
    // 如果可以开火且当前弹药数大于0
    if (canfire){
    
    
        if (usinggunbulets[(int)Guntype.pistol] > 0){
    
    
            // 播放枪声音效,设置开火标志位为false,延迟一段时间后设置为true,减少当前弹药数,显示弹药数信息,进行射线检测
            gunsource.clip = gunclip[0];
            gunsource.Play();
            canfire = false;
            Invoke("Canfire", cooltime[(int)Guntype.pistol]);
            usinggunbulets[(int)Guntype.pistol]--;
            showbulletsinfo((int)Guntype.pistol);
            castray();
        }
    }
}

// 冲锋枪开火函数
private void tommyfire(){
    
    
    if (canfire){
    
    
        // 播放枪声音效,延迟一段时间后再次调用tommyfire函数进行连发,减少当前弹药数,显示弹药数信息,进行射线检测
        gunsource.clip = gunclip[1];
        gunsource.Play();
        Invoke("tommyfire", cooltime[(int)Guntype.tommy]);
        usinggunbulets[(int)Guntype.tommy]--;
        showbulletsinfo((int)Guntype.tommy);
        castray();
    }
}

// 停止射击按钮响应函数
public void Button_stopfire(){
    
    
    if (usinggun == Guntype.tommy){
    
    
        // 如果当前使用的是冲锋枪,则设置开火标志位为false,延迟一段时间后设置为true
        canfire = false;
        Invoke("Canfire", 0.3f);
    }
}

// 步枪开火函数
private void riflefire(){
    
    
if (canfire){
    
    
if (usinggunbulets[(int)Guntype.rifle] == 0)
return;
// 播放枪声音效,设置开火标志位为false,延迟一段时间后设置为true,减少当前弹药数,显示弹药数信息,进行射线检测
gunsource.clip = gunclip[2];
gunsource.Play();
canfire = false;
Invoke("Canfire", cooltime[(int)Guntype.rifle]);
usinggunbulets[(int)Guntype.rifle]--;
showbulletsinfo((int)Guntype.rifle);
castray();
}
}

clojure
Copy
// 选择枪支按钮响应函数
public void Button_select(int gunid){
    
    
    // 根据选择的枪支id设置当前使用的枪支类型,并显示弹药数信息
    switch(gunid){
    
    
        case 0:
            usinggun = Guntype.pistol;
            showbulletsinfo((int)Guntype.pistol);
            break;
        case 1:
            usinggun = Guntype.tommy;
            showbulletsinfo((int)Guntype.tommy);
            break;
        case 2:
            usinggun = Guntype.rifle;
            showbulletsinfo((int)Guntype.rifle);
            break;
        default :
            break;
    }
}

// 显示当前枪支弹药数信息
private void showbulletsinfo(int gunid){
    
    
    showbullets.text = usinggunbulets[gunid].ToString() + "/" + clapbullets[gunid];
}

// 显示总弹药数信息
private void showtotalbullets(){
    
    
    totalbulletstext[0].text = totalbullets[0].ToString();
    totalbulletstext[1].text = totalbullets[1].ToString();
    totalbulletstext[2].text = totalbullets[2].ToString();
}

// 设置开火标志位为true的函数
private void Canfire(){
    
    
    canfire = true;
}

// 重新装填按钮响应函数
public void Button_Reload(){
    
    
    // 根据当前使用的枪支id执行重新装填操作
    Reload((int)usinggun);
}

// 重新装填函数
public void Reload(int gunid){
    
    
    // 计算需要重新装填的弹药数,如果总弹药数足够,则重新装填弹药,否则只装填剩余的总弹药数,同时更新显示的弹药数信息和总弹药数信息
    int cout = clapbullets[gunid] - usinggunbulets[gunid];
    if (cout <= totalbullets[gunid]){
    
    
        usinggunbulets[gunid] = clapbullets[gunid];
        totalbullets[gunid] -= cout;
    }
    else{
    
    
        totalbullets[gunid] += totalbullets[gunid];
        totalbullets[gunid] = 0;
    }
    showbulletsinfo(gunid);
    showtotalbullets();
}

// 射线检测函数,检测是否射中了敌人
private void castray(){
    
    
    Vector2 v = new Vector2 (Screen.width/2, Screen.height/2);
    RaycastHit hit;
    if (Physics.Raycast(Camera.main.ScreenPointToRay(v), out hit)){
    
    
        if (hit.transform.root.CompareTag("zombie")){
    
    
            // 如果射中了敌人,则播放血液特效并造成伤害
            bloodzone.SetActive(true);
            var zombie = hit.transform.root.GetComponent<health>();
            if (hit.transform.name == "Bip001 Pelvis"){
    
    
                zombie.getdamage(gundamage[(int)usinggun]);
            }
            else{
    
    
                zombie.getdamage(gundamage[(int)usinggun] * 3);
            }
        }
        else{
    
    
            // 如果没有射中敌人,则隐藏血液特效
            bloodzone.SetActive(false);
        }
    }
}
}

准星控制射击

在这里插入图片描述

private void castray(){
    
    
    Vector2 v = new Vector2(Screen.width/2,Screen.height/2); // 创建一个屏幕中心点的 2D 向量 v
    RaycastHit hit; // 创建一个 RaycastHit 变量用于接收射线的碰撞结果
    
    if (Physics.Raycast(Camera.main.ScreenPointToRay(v), out hit)) // 从相机发出一条射线,通过屏幕中心点 v,检测是否与物体发生碰撞,并将结果保存在 hit 变量中
    {
    
    
        if (hit.transform.root.CompareTag("zombie")) // 如果射线碰撞的物体的根节点标签是 "zombie"
        {
    
    
            bloodzone.SetActive(true); // 激活一个名为 "bloodzone" 的游戏对象

            var zombie = hit.transform.root.GetComponent<health>(); // 获取射线碰撞物体的根节点上的 health 组件,赋值给 zombie 变量

            if (hit.transform.name == "Bip001 Pelvis") // 如果射线碰撞到的物体的名字是 "Bip001 Pelvis"
            {
    
    
                zombie.getdamage(gundamage[(int)usinggun]); // 调用 zombie 的 getdamage 方法,并传入 usinggun 变量对应的 gundamage 值作为参数
            }
            else
            {
    
    
                zombie.getdamage(gundamage[(int)usinggun] * 3); // 否则,调用 zombie 的 getdamage 方法,并传入 usinggun 变量对应的 gundamage 值乘以 3 作为参数
            }
        }
        else
        {
    
    
            bloodzone.SetActive(false); // 如果射线碰撞的物体不是 "zombie",则关闭 "bloodzone" 对象
        }
    }
}

这段代码的作用是进行射线检测,判断射线是否与名为 “zombie” 的物体发生碰撞。如果发生碰撞,则激活名为 “bloodzone” 的游戏对象,并根据射线碰撞的具体位置和物体的名称,调用相应的伤害方法。如果没有发生碰撞或者射线碰撞的物体不是 “zombie”,则关闭 “bloodzone” 对象。


僵尸动画、血条

在这里插入图片描述

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

public class health : MonoBehaviour
{
    
    
    public float hitpot;  // 角色的当前生命值
    public float maxpot = 100f;  // 角色的最大生命值
    public Slider blood;  // 血条UI元素
    public player player;  // player脚本的引用
    public zombie zom;  // zombie脚本的引用

    public Sprite headcon;  // 头像图标的Sprite
    public Image headimage;  // 头像UI元素的Image组件

    void Start()
    {
    
    
        hitpot = maxpot;  // 设置初始生命值为最大生命值
        blood.maxValue = maxpot;  // 设置血条的最大值为最大生命值
        blood.value = maxpot;  // 设置血条的当前值为最大生命值
        blood.gameObject.SetActive(false);  // 隐藏血条
        headimage.gameObject.SetActive(false);  // 隐藏头像
    }

    void Update()
    {
    
    
        if (zom.agent.enabled)
        {
    
    
            zom.agent.SetDestination(zom.player.position);  // 设置僵尸的目标位置为玩家的位置
        }
        if (Vector3.Distance(zom.transform.position, zom.player.position) <= 1.8)
        {
    
    
            zom.zombieanimator.SetBool("attack", true);  // 设置僵尸的攻击动画为true
        }
        else
        {
    
    
            zom.zombieanimator.SetBool("attack", false);  // 设置僵尸的攻击动画为false
            zom.zombieanimator.SetBool("walk", true);  // 设置僵尸的行走动画为true
        }
    }

    public void getdamage(float dam)
    {
    
    
        blood.gameObject.SetActive(true);  // 显示血条
        headimage.gameObject.SetActive(true);  // 显示头像

        headimage.sprite = headcon;  // 设置头像图标为指定的Sprite

        hitpot -= dam;  // 减去受到的伤害值
        blood.value = hitpot;  // 更新血条的当前值为剩余生命值

        if (hitpot <= 0)
        {
    
    
            GetComponent<Animator>().SetBool("dead", true);  // 设置角色的死亡动画为true
            zom.agent.enabled = false;  // 禁用僵尸的导航代理
        }
    }
}


设置导航

在这里插入图片描述

 void Update()
    {
    
    
        if (zom.agent.enabled)
        {
    
    
            zom.agent.SetDestination(zom.player.position);  // 设置僵尸的目标位置为玩家的位置
        }
        if (Vector3.Distance(zom.transform.position, zom.player.position) <= 1.8)
        {
    
    
            zom.zombieanimator.SetBool("attack", true);  // 设置僵尸的攻击动画为true
        }
        else
        {
    
    
            zom.zombieanimator.SetBool("attack", false);  // 设置僵尸的攻击动画为false
            zom.zombieanimator.SetBool("walk", true);  // 设置僵尸的行走动画为true
        }
    }

猜你喜欢

转载自blog.csdn.net/qq_20179331/article/details/131449301