血条UI操作
渲染模式改为世界空间
导入面板组件并通过画布调整大小
在面板上导入文本组件,显示玩家名称(旧版)
为了使血条填满,Fill Area 数值全部置为0
改变背景颜色和填充物颜色,并设置血条方向
改变数值方便计算
绑定文本组件和滑动条组件
复活
创建一个空物体,加Respawn标签,意为复活点
可以在此处旋转标记,该处标记只有程序员能看见
绑定复活效果特效
代码
掉血,重生
PlayerHealth脚本(绑定在人物身上)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PlayerHealth : MonoBehaviour
{
public Text nameText;//玩家名称
public Slider healthSlider;
public float maxHp = 100;
public float minHp = 0;
public float currentHp;//当前的血量
public Transform RespawnPoint;//重生点
public GameObject respawnVFX;//重生特效
void Start()
{
if (RespawnPoint == null)
{
RespawnPoint = GameObject.FindGameObjectWithTag("Respawn").transform;
}
if (nameText == null || healthSlider == null||respawnVFX==null)
{
Debug.LogError("请检查文本或滑动条组件或重生特效粒子拽了没");
}
if (nameText != null)
{
nameText.text = "玩家小组名称";
}
currentHp = maxHp;//开始满血
if (healthSlider != null)
{
healthSlider.maxValue = maxHp;
healthSlider.value = currentHp;
}
}
public void TakeDamage(float damage)
{
if (currentHp == 0)
{
return;
}
if (currentHp > damage)
{
currentHp -= damage;
}
else
{
currentHp = 0;
}
if (currentHp == 0)
{
print("死掉咯");
//隐藏玩家
gameObject.SetActive(false);
//延迟调用
//Invoke("PlayerRespawn", 5f);
PlayerRespawn();
}
healthSlider.value = currentHp;
}
//
void ShowPlayer()
{
gameObject.SetActive(true);
}
//玩家死亡重生后
public void PlayerRespawn()
{
currentHp = 100;
//同步UI数据
healthSlider.value = currentHp;
//玩家瞬移到重生点,并实例化重生特效
if (RespawnPoint != null)
{
transform.position = RespawnPoint.position;
if (respawnVFX != null)
{
//实例化特效粒子
GameObject respawnEffect = Instantiate(respawnVFX, transform.position, Quaternion.identity);
Invoke("ShowPlayer", 2f);
Destroy(respawnEffect,4f);
}
}
}
}
捡起丢弃道具
CanPickUpItem脚本(绑定在玩家身上)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CanPickUpItem : MonoBehaviour
{
public bool isNear = false;//玩家是否在附近
public Transform rHandItemPos;//玩家右手位置
public bool rHandIsHasItem=false;//玩家手中是否有道具
private void Start()
{
if (rHandItemPos == null)
{
Debug.LogError("检查参数栏,道具位置物体没有拽入");
}
}
private void OnTriggerEnter(Collider other)
{
if(other.CompareTag("Player"))
{
isNear = true;
}
}
private void OnTriggerExit(Collider other)
{
if (other.CompareTag("Player"))
{
isNear = false;
}
}
private void Update()
{
//如果玩家到达附加并且按下键盘上的E键,拾取道具
if(isNear && Input.GetKeyDown(KeyCode.E))
{
if(!rHandIsHasItem)
{
PickUpItem();
}
/* else
{
Debug.LogError("玩家手里已经有了其他道具,不能重复拾取");
}*/
}
if(Input.GetKeyDown(KeyCode.G)&&rHandIsHasItem)
{
DisCardItem();
}
}
//拾取道具(坐标的转移)
void PickUpItem()
{
rHandIsHasItem = true;
transform.position=rHandItemPos.position;//位置转移
transform.rotation=rHandItemPos.rotation;//角度转移
//打开刚体的忽略物理学运算开关
transform.GetComponent<Rigidbody>().isKinematic = true;
//关闭碰撞器开关
transform.GetComponent<BoxCollider>().enabled=false;
//关闭触发器开关
transform.GetComponent<SphereCollider>().enabled=false;
//把玩家的手和道具绑定在一起(设置右手位置物体,为道具的父物体)
transform.SetParent(rHandItemPos);
}
//丢弃道具
void DisCardItem()
{
if(rHandIsHasItem)
{
rHandIsHasItem = false;
//关闭刚体的忽略物理学运算开关
transform.GetComponent<Rigidbody>().isKinematic = false;
//加力
transform.GetComponent<Rigidbody>().AddForce(Vector3.forward*2000f);
//打开碰撞器开关
transform.GetComponent<BoxCollider>().enabled = true;
//打开触发器开关
transform.GetComponent<SphereCollider>().enabled = true;
//把玩家的手和道具解绑
transform.SetParent(null);
}
}
}
视角的移动
CameraMove脚本(绑定在摄像机身上)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 第三人称摄像机简单版
/// </summary>
public class CameraMove : MonoBehaviour
{
public Transform target;//摄像机的跟随目标
public float distance = 8.0f;//摄像机与目标之间的距离
private float x, y, z;
private float xSpeed = 250.0f;
private float ySpeed = 120.0f;
//限制上下移动角度
public float yMinlimit = -45.0f;
public float yMaxlimit = 45.0f;
/* private void Awake()
{
//注册场景加载完毕事件
EventCenter.AddListener(EventType.SceneLoadComplete, SetTarget);
}*/
private void SetTarget()
{
//将标签为Player的物体设置为跟踪目标
Transform player = GameObject.FindGameObjectWithTag("Player").transform;
if (player != null && target == null)
target = player;
}
private void Start()
{
Vector3 angles = transform.eulerAngles;//获取摄像机的当前角度
x = angles.x;
y = angles.y;
z = -distance;
GoRight();
}
private void LateUpdate()
{
float temp = Input.GetAxis("Mouse ScrollWheel");//获取滚轮数值
if (target != null)
{
if (Input.GetMouseButton(0))
{
x += Input.GetAxis("Mouse X") * xSpeed * 0.02f;
y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f;
}
/*
else if (Input.GetMouseButton(1))
{
x += Input.GetAxis("Mouse X") * xSpeed * 0.02f;
y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f;
//影响玩家的Y轴旋转角度
target.rotation = Quaternion.Euler(target.rotation.x,x,target.rotation.z);
}
*/
}
//钳制上下移动的角度
y = ClampAngle(y,yMinlimit,yMaxlimit);
z += temp * 100f * 0.02f;//数值按照自己喜好设定
z = Mathf.Clamp(z,-20f,-3.0f);//距离限制,最远是距离玩家20米,最近是3米
GoRight();//作用于摄像机
}
float ClampAngle(float angle,float min,float max)
{
if (angle < -360)
angle += 360;
if (angle > 360)
angle -= 360;
return Mathf.Clamp(angle,min,max);
}
//摄像机控制位置及角度的核心方法
void GoRight()
{
if (target == null)
return;
Quaternion rotation = Quaternion.Euler(y,x,0);//摄像机角度
Vector3 position = rotation * new Vector3(0.0f,0.0f,z)+target.position;
transform.position = position;//摄像机位置
transform.rotation = rotation;//摄像机角度
}
}
怪物的按路径移动,攻击,闲置状态
MonsterAI脚本(绑定在怪物身上)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using static UnityEditor.FilePathAttribute;
public enum MonstorAIState
{
idle,//闲置状态
Patrol,//巡逻
ChasePlayer,//追击玩家
AttackPlayer,//攻击玩家
//自己添加其他状态
}
public class MonsterAI : MonoBehaviour
{
public float speed = 2f;//移动速度
public Transform[] wayPointArr;//路径点数组
public Transform pathObj;//路径点父物体
//当前的路径点下标
public int currentWaypointIndex = 0;
private Animator _animator;//动画器
public MonstorAIState monsterCurrentState=0;//怪物当前状态
public MonstorAIState monsterOldState=0;
private Transform playerObj;//玩家物体
public bool isAlive=true;//丧尸是否活着
public bool isFindPlayer = false;//是否发现玩家
// Start is called before the first frame update
void Start()
{
//在场景里通过标签查找玩家物体
playerObj = GameObject.FindGameObjectWithTag("Player").transform;
//判断玩家是否已经正确找到
if(playerObj==null)
{
Debug.LogError("没有找到玩家物体,请查看玩家标签是否正确设置为Player");
}
_animator = GetComponent<Animator>();//获取动画器组件引用
//自动搜索场景中的路径点加载到路径点数组中
if (pathObj != null)
{
//数组开辟内存
wayPointArr = new Transform[pathObj.childCount];
for (int i = 0; i < pathObj.childCount; i++)
{
wayPointArr[i] = pathObj.GetChild(i).transform;
}
}
else
{
Debug.LogError("路径点父物体没有拽入参数栏");
}
//开启协程
StartCoroutine(IsFindPlayer(1f,25f));
}
// Update is called once per frame
void Update()
{
//如果丧失已经死亡,则不郧西下面代码
if (!isAlive)
return;
switch (monsterCurrentState)
{
case MonstorAIState.idle:
Idle();
break;
case MonstorAIState.Patrol://巡逻状态
Patrol();
break;
case MonstorAIState.ChasePlayer://追击玩家
ChasePlayer();
break;
case MonstorAIState.AttackPlayer://追击玩家
AttackPlayer();
break;
}
}
void Patrol()//巡逻状态
{
speed = 0.5f;
_animator.SetFloat("moveSpeed",speed);
//计算怪物与目标点之间的角度 并进行方向修正
Vector3 direction = wayPointArr[currentWaypointIndex].position - transform.position;
//计算旋转角度并旋转
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(direction), 0.1f);
if (direction.sqrMagnitude < 0.02f)
{
if (currentWaypointIndex++ == wayPointArr.Length - 1)
{
currentWaypointIndex = 0;
}
}
//一直前进
transform.Translate(Vector3.forward * Time.deltaTime * speed);
//播放动画
_animator.SetFloat("moveSpeed", speed);
}
//闲置
void Idle()
{
speed = 0;
_animator.SetFloat("moveSpeed",speed);
}
//追击玩家方法
void ChasePlayer()
{
speed = 0.8f;
_animator.SetFloat("moveSpeed", speed);
//面向玩家
//计算与玩家的角度
Vector3 direction = playerObj.position - transform.position;
//计算旋转角度并旋转
transform.rotation = Quaternion.Slerp(transform.rotation,
Quaternion.LookRotation(direction),
0.1f);
//一直前进
transform.Translate(Vector3.forward * Time.deltaTime * speed);
//播放动画
_animator.SetFloat("moveSpeed", speed);
//如果玩家与怪物之间的距离大于设定的N米,则返回旧状态
if(direction.sqrMagnitude>10f*10f)
{
monsterCurrentState = monsterOldState;
isFindPlayer = false;//玩家追丢了,重置
}
//查找玩家的协程有没有开启
//如果玩家与怪物之间的距离小于设定的距离,则进入到攻击状态
print(direction.sqrMagnitude);
if(direction.sqrMagnitude<0.4f)
{
monsterCurrentState=MonstorAIState.AttackPlayer;//攻击玩家
}
}
//通过与玩家距离来判断是否发现玩家
IEnumerator IsFindPlayer(float intervalTime, float discoveryDistance)
{
while (isAlive)
{
if (!isFindPlayer && playerObj != null)
{//计算与玩家的距离
Vector3 direction = playerObj.position - transform.position;
if (direction.sqrMagnitude < discoveryDistance*discoveryDistance)
{
//发现玩家
print("怪物已经发现玩家!");
isFindPlayer = true;
//记录旧状态
monsterOldState = monsterCurrentState;
//将当前的怪物状态设置为追击玩家状态
monsterCurrentState = MonstorAIState.ChasePlayer;
}
/* else
{
//将当前的怪物状态设置为闲置状态
monsterCurrentState = MonstorAIState.idle;
}*/
}
yield return new WaitForSeconds(intervalTime);
}
}
void AttackPlayer()
{
speed = 0;
_animator.SetFloat("moveSpeed", speed);
_animator.SetTrigger("Attack");//播放攻击玩家动画
//计算与玩家的距离
Vector3 direction = playerObj.position - transform.position;
if(direction.sqrMagnitude>0.4f)
{
monsterCurrentState = MonstorAIState.ChasePlayer;
}
}
}
在距离小于3f时,调用伤害方法
MonsterAnimationEvent脚本(绑定在怪物身上)
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class MonsterAnimationEvent : MonoBehaviour
{
public GameObject playerObj;
private void Start()
{
if (playerObj == null)
{
playerObj = GameObject.FindGameObjectWithTag("Player");
}
}
public void AttackPlayer(float damage)
{
if (playerObj != null)
{
float distance = Vector3.Distance(playerObj.transform.position, transform.position);
// 打印距离
Debug.Log(distance);
if (distance < 3f)
{
playerObj.GetComponent<PlayerHealth>().TakeDamage(damage);
}
}
}
}