文章目录
- 介绍
- 制作基本UI
- 枚举控制角色移动
- 切枪、设置音效、设置子弹威力、设置子弹时间间隔、换弹
- 准星控制射击
- 僵尸动画、血条
- 设置导航
介绍
利用协程、枚举、动画器、导航等知识点。
实现移动、切枪、换弹、射击、僵尸追踪、攻击。
制作基本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
}
}