【Unity3D】血条(HP)
1 需求实现
人机交互Input 中实现了通过键盘控制坦克运动,通过鼠标控制坦克发射炮弹,本文将在此基础上,增加血条(HP)功能。炮弹命中后,HP 值会减少,因此需要应用到 刚体组件Rigidbody 和 碰撞体组件Collider;从不同角度攻击敌人时,敌人的血条始终朝向相机,因此需要用到 相机跟随;血条通过 Image 显示,因此需要用到 UGUI之Image;玩家的血条始终显示在屏幕左上角,因此需要使用到 锚点。
1)需求实现
- 前后箭头键或 W, S 键控制玩家前进或后退;
- 左右箭头键或 A, D 键控制玩家左右转向;
- 鼠标左键或空格键控制玩家发射炮弹;
- 玩家血条显示在屏幕左上角;
- 相机在玩家后上方的位置,始终跟随玩家,朝玩家正前方看;
- 玩家移动时,敌人转向玩家,当偏离玩家的角度小于5°时,发射炮弹;
- 敌人血条显示在其上方,并且始终看向相机。
2)涉及技术栈
本文代码资源见 → Unity3D 血条效果。
2 游戏对象
1)游戏界面

2)游戏对象层级结构

3)Transform组件参数
\1. 玩家 Transform 组件参数
| Name | Type | Position | Rotation | Scale | Color/Texture |
|---|---|---|---|---|---|
| Player | Empty | (0, 0.25, -5) | (0, 0, 0) | (1, 1, 1) | #228439FF |
| Botton | Cube | (0, 0, 0) | (0, 0, 0) | (2, 0.5, 2) | #228439FF |
| Top | Cube | (0, 0.5, 0) | (0, 0, 0) | (1, 0.5, 1) | #228439FF |
| Gun | Cylinder | (0, 0, 1.5) | (90, 0, 0) | (0.2, 1, 0.4) | #228439FF |
| FirePoint | Empty | (0, 1.15, 0) | (0, 0, 0) | (1, 1, 1) | —— |
补充:Player 游戏对象添加了刚体组件,并修改 Mass = 100,Drag = 1,AngularDrag = 0.1,Freeze Rotation 中勾选 X 和 Z。
\2. 玩家 HP RectTransform 组件参数
| Name | Type | Rect | Width/Height | Pos | Color/Texture |
|---|---|---|---|---|---|
| PlayerHP | Canvas | —— | —— | —— | —— |
| Panel | Panel | (0, 0, 0, 0) | —— | (-, -, 0) | #FFFFFF00 |
| HealthBG | Image | (0, 0.25, -5) | (200, 20) | (125, -30, 0) | #FFFFFFFF |
| Health | Image | (0, 0.25, -5) | (200, 20) | (125, -30, 0) | #FF2230FF |
补充: 玩家 PlayerHP 的 Canvas 渲染模式是 Screen Space - Overlay,Health 的 ImageType 设置为 Filled,Fill Method 设置为 Horizontal。
\3. 敌人 Transform 组件参数
| Name | Type | Position | Rotation | Scale | Color/Texture |
|---|---|---|---|---|---|
| Enemy | Empty | (0, 0.25, 5) | (0, 180, 0) | (1, 1, 1) | #15D3F9FF |
| Botton | Cube | (0, 0, 0) | (0, 0, 0) | (2, 0.5, 2) | #15D3F9FF |
| Top | Cube | (0, 0.5, 0) | (0, 0, 0) | (1, 0.5, 1) | #15D3F9FF |
| Gun | Cylinder | (0, 0, 1.5) | (90, 0, 0) | (0.2, 1, 0.4) | #15D3F9FF |
| FirePoint | Empty | (0, 1.15, 0) | (0, 0, 0) | (1, 1, 1) | —— |
补充:Enemy 游戏对象添加了刚体组件,并修改 Mass = 100,Drag = 0.5,AngularDrag = 0.1,Freeze Rotation 中勾选 X 和 Z。
\4. 敌人 HP RectTransform 组件参数
| Name | Type | Width/Height | Pos | Color/Texture |
|---|---|---|---|---|
| HP | Canvas | (2, 0.2) | (0, 0.85, 0) | —— |
| HealthBG | Image | (2, 0.2) | (0, 0, 0) | #FFFFFFFF |
| Health | Image | (2, 0.2) | (0, 0, 0) | #FF2230FF |
补充: 敌人 HP 的 Canvas 渲染模式是 World Space,Health 的 ImageType 设置为 Filled,Fill Method 设置为 Horizontal。
\5. 地面和炮弹 Transform 组件参数
| Name | Type | Position | Rotation | Scale | Color/Texture |
|---|---|---|---|---|---|
| Plane | Plane | (0, 0, 0) | (0, 0, 0) | (10, 10, 10) | GrassRockyAlbedo |
| Bullet | Sphere | (0, 0.5, -5) | (0, 0, 0) | (0.3, 0.3, 0.3) | #228439FF |
补充:炮弹作为预设体拖拽到 Assets/Resources/Prefabs 目录下,并且添加了刚体组件。
3 脚本组件
1)CameraController
CameraController.cs
using UnityEngine;
public class CameraController : MonoBehaviour {
private Transform player; // 玩家
private Vector3 relaPlayerPos; // 相机在玩家坐标系中的位置
private float targetDistance = 15f; // 相机看向玩家前方的位置
private void Start() {
relaPlayerPos = new Vector3(0, 4, -8);
player = GameObject.Find("Player/Top").transform;
}
private void LateUpdate() {
CompCameraPos();
}
private void CompCameraPos() { // 计算相机坐标
Vector3 target = player.position + player.forward * targetDistance;
transform.position = transformVecter(relaPlayerPos, player.position, player.right, player.up, player.forward);
transform.rotation = Quaternion.LookRotation(target - transform.position);
}
// 求以origin为原点, locX, locY, locZ 为坐标轴的本地坐标系中的向量 vec 在世界坐标系中对应的向量
private Vector3 transformVecter(Vector3 vec, Vector3 origin, Vector3 locX, Vector3 locY, Vector3 locZ) {
return vec.x * locX + vec.y * locY + vec.z * locZ + origin;
}
}
说明: CameraController 脚本组件挂在 MainCamera 游戏对象上。
2)PlayerController
PlayerController.cs
using System;
using UnityEngine;
public class PlayerController : MonoBehaviour {
private Transform firePoint; // 开火点
private GameObject bulletPrefab; // 炮弹预设体
private float tankMoveSpeed = 4f; // 坦克移动速度
private float tankRotateSpeed = 2f; // 坦克转向速度
private float fireWaitTime = float.MaxValue; // 距离上次开火已等待的时间
private float bulletCoolTime = 0.15f; // 炮弹冷却时间
private void Start() {
firePoint = transform.Find("Top/Gun/FirePoint");
bulletPrefab = (GameObject) Resources.Load("Prefabs/Bullet");
}
private void Update() {
fireWaitTime += Time.deltaTime;
float hor = Input.GetAxis("Horizontal");
float ver = Input.GetAxis("Vertical");
Move(hor, ver);
if (Input.GetMouseButtonDown(0) || Input.GetKeyDown(KeyCode.Space)) {
Fire();
}
}
private void Move(float hor, float ver) { // 坦克移动
if (Math.Abs(hor) > 0.1f || Math.Abs(ver) > 0.1f) {
GetComponent<Rigidbody>().velocity = transform.forward * tankMoveSpeed * ver;
GetComponent<Rigidbody>().angularVelocity = Vector3.up * tankRotateSpeed * hor;
}
}
private void Fire() { // 开炮
if (fireWaitTime > bulletCoolTime) {
BulletInfo bulletInfo = new BulletInfo("PlayerBullet", Color.red, transform.forward, 10f, 15f);
GameObject bullet = Instantiate(bulletPrefab, firePoint.position, Quaternion.identity);
bullet.AddComponent<BulletController>().SetBulletInfo(bulletInfo);
fireWaitTime = 0f;
}
}
}
说明: PlayerController 脚本组件挂在 Player 游戏对象上。
3)EnemyController
EnemyController.cs
using UnityEngine;
using UnityEngine.UI;
public class EnemyController : MonoBehaviour {
private Transform target; // 目标
private Transform top; // 炮头
private Transform firePoint; // 开火点
private Transform hp; // 血条
private GameObject bulletPrefab; // 炮弹预设体
private float rotateSpeed = 0.4f; // 坦克转向速度
private float fireWaitTime = float.MaxValue; // 距离上次开火已等待的时间
private float bulletCoolTime = 1f; // 炮弹冷却时间
private void Start () {
target = GameObject.Find("Player/Top").transform;
top = transform.Find("Top");
firePoint = transform.Find("Top/Gun/FirePoint");
hp = transform.Find("HP");
bulletPrefab = (GameObject) Resources.Load("Prefabs/Bullet");
}
private void Update () {
fireWaitTime += Time.deltaTime;
LookAtTarget();
float angle = Vector3.Angle(target.position - top.position, top.forward);
if (LookAtTarget()) {
Fire();
}
HPLookAtCamera();
}
private bool LookAtTarget() {
Vector3 dir = target.position - top.position;
float angle = Vector3.Angle(dir, top.forward);
if (angle > 5) {
int axis = Vector3.Dot(Vector3.Cross(dir, top.forward), Vector3.up) > 0 ? -1 : 1;
GetComponent<Rigidbody>().angularVelocity = axis * Vector3.up * rotateSpeed;
return false;
}
GetComponent<Rigidbody>().angularVelocity = Vector3.zero;
return true;
}
private void HPLookAtCamera() {
Vector3 cameraPos = Camera.main.transform.position;
Vector3 target = new Vector3(cameraPos.x, hp.position.y, cameraPos.z);
hp.LookAt(target);
}
private void Fire() {
if (fireWaitTime > bulletCoolTime) {
BulletInfo bulletInfo = new BulletInfo("EnemyBullet", Color.yellow, top.forward, 5f, 10f);
GameObject bullet = Instantiate(bulletPrefab, firePoint.position, Quaternion.identity); // 通过预设体创建炮弹
bullet.AddComponent<BulletController>().SetBulletInfo(bulletInfo);
fireWaitTime = 0;
}
}
}
说明: EnemyController 脚本组件挂在 Enemy 游戏对象上。
4)BulletController
BulletController.cs
using UnityEngine;
using UnityEngine.UI;
public class BulletController : MonoBehaviour {
private BulletInfo bulletInfo; // 炮弹信息
private volatile bool isDying = false;
private void Start () {
gameObject.name = bulletInfo.name;
GetComponent<MeshRenderer>().material.color = bulletInfo.color;
float lifeTime = bulletInfo.fireRange / bulletInfo.speed; // 存活时间
Destroy(gameObject, lifeTime);
}
private void Update () {
transform.GetComponent<Rigidbody>().velocity = bulletInfo.flyDir * bulletInfo.speed;
}
private void OnCollisionEnter(Collision other) {
if (isDying) {
return;
}
if (IsHitEnemy(gameObject.name, other.gameObject.name)) {
other.transform.Find("HP/Health").GetComponent<Image>().fillAmount -= 0.1f;
isDying = true;
Destroy(gameObject, 0.1f);
} else if (IsHitPlayer(gameObject.name, other.gameObject.name)) {
GameObject.Find("PlayerHP/Panel/Health").GetComponent<Image>().fillAmount -= 0.1f;
isDying = true;
Destroy(gameObject, 0.1f);
}
}
public void SetBulletInfo(BulletInfo bulletInfo) {
this.bulletInfo = bulletInfo;
}
private bool IsHitEnemy(string name, string otherName) { // 射击到敌军
return name.Equals("PlayerBullet") && otherName.Equals("Enemy");
}
private bool IsHitPlayer(string name, string otherName) { // 射击到玩家
return name.Equals("EnemyBullet") && otherName.Equals("Player");
}
}
说明: BulletController 脚本组件挂在 Bullet 游戏对象上(代码里动态添加)。
5)BulletInfo
BulletInfo.cs
using UnityEngine;
public class BulletInfo {
public string name; // 炮弹名
public Color color; // 炮弹颜色
public Vector3 flyDir; // 炮弹飞出方向
public float speed; // 炮弹飞行速度
public float fireRange; // 炮弹射程
public BulletInfo(string name, Color color, Vector3 flyDir, float speed, float fireRange) {
this.name = name;
this.color = color;
this.flyDir = flyDir;
this.speed = speed;
this.fireRange = fireRange;
}
}
4 运行效果

声明:本文转自【Unity3D】血条(HP)
【Unity3D】血条(HP)的更多相关文章
- Unity3D人物头顶名称与血条更新与绘制
using UnityEngine; using System.Collections; public class NPC : MonoBehaviour { //主摄像机对象 private Cam ...
- Unity3D NGUI学习(一)血条
这次来讲讲Unity3D NGUI这个插件的学习,这个插件是收费的,不过去网上可以下载得很多可用版本.用来做用户的交互UI,学习起来比较简单 第一步,导入NGUI包 http://pan.baidu. ...
- Unity3D 人形血条制作小知识
这几天用Unity3D做个射击小游戏,想做个人形的血条.百思不得其解,后来问了网上的牛牛们,攻克了,事实上挺简单的,GUI里面有个函数DrawTextureWithTexCoords就能够实现图片的裁 ...
- Shader实例:NGUI制作网格样式血条
效果: 思路: 1.算出正确的uv去采样过滤图,上一篇文章说的很明白了.Shader实例:NGUI图集中的UISprite正确使用Shader的方法 2.用当前血量占总血量的百分比来设置shader中 ...
- UE4 使用UGM制作血条
声明:本文是自己做的项目,可能不是最好的方法,或有错误使用方式.各位不喜勿喷! HP进度 HP背景 将上面的资源拖到UE4中(使用UE4自带的颜色也可实现效果,具体参考官方教程 https://doc ...
- 【FairyGUI & Unity】实现血条UI扣血与加血的缓动效果
组件设计 创建一个进度条组件,作为血条. bar是实际血量条 DownBar是扣血缓动背景图层 UpBar是加血缓动背景图层 LowBar是低血量变色(和控制器配合,本文不讲) n11组合是血量参考线 ...
- unity3d-游戏实战突出重围,第二天 制作血条
using UnityEngine; using System.Collections; public class xt : MonoBehaviour { //红色血条 public Texture ...
- unity如何显示血条(不使用NGUI)
用unity本身自带的功能,如何显示血条? 显示血条,从资源最小化的角度,只要把一个像素的色点放大成一个矩形就足够,三个不同颜色的矩形,分别显示前景色,背景色,填充色,这样会消耗最少的显存资源. un ...
- Unity UGUI HUD 怪物血条实现
首先做一个血条,创建一个名为Follow3DObject的脚本添加到血条控件上. Follow3DObject.cs的代码如下: using UnityEngine; using System.Col ...
- 使用 NGUI 实现头顶文字及血条
以下是 NGUI HUD Text 实现的: 基本原理: 1. 在角色头顶绑一个点 Pivot,用于对齐 2. 因为界面总是覆盖在人物头顶信息的上面,所以将 UIRoot 分为2个 Panel:1) ...
随机推荐
- 使用VS开发人员工具观察类在内存中的布局
1.先要生成相应文件 2.打开VS2019开发人员工具 3.cd至文件目录 4.输入cl /d1 reportSingleClassLayoutanimal demo.cpp 其中reportSing ...
- 【PHP】 延时跳转
echo "<meta http-equiv=\"refresh\" content=\"5;url="."register.php& ...
- 如何查找SpringBoot应用中的请求路径(不使用idea)
背景 昨天有个同事向我咨询某个接口的物理表是哪个,由于公司业务较多.这块业务的确不是我负责的,也没有使用idea不能全局搜索(eclipse搜不到jar内的字符串),也就回复了不清楚. 除了自己写代码 ...
- CoreDNS -- DNS服务与服务发现
CoreDNS -- DNS服务与服务发现 DNS服务器 手册:https://coredns.io/manual/toc/ Github:https://github.com/coredns/cor ...
- C#调用C++——CLR方式
一直是在写C#,最近接触到的项目中有C#调用C++接口的逻辑,自己学习了下,写个步骤日志,C#掉用C++的托管代码 项目分三个项目:1.底层C++动态库项目,2.中间层的CLR项目,3.上层的C#项目 ...
- Redis监控方法之二
Redis监控方法之二 背景 前期整理过使用 exporter + prometheus 方式进行Redis监控的搭建过程 最近给同事研究clickhouse时发现 clickhouse 有对应的pl ...
- [转帖]redis缓存命中率介绍
缓存命中率的介绍 命中:可以直接通过缓存获取到需要的数据. 不命中:无法直接通过缓存获取到想要的数据,需要再次查询数据库或者执行其它的操作.原因可能是由于缓存中根本不存在,或者缓存已经过期. 通常来讲 ...
- [转帖]10--k8s之数据持久化
https://www.cnblogs.com/caodan01/p/15136217.html 目录 一.emptDir 二.hostPath 三.pv 和 pvc 1.环境准备 2.创建pv 3. ...
- [转帖]45个处理字符串的Python方法
https://baijiahao.baidu.com/s?id=1738413163267646541&wfr=spider&for=pc 一.题目解析 先来看一个题目: 判断用 ...
- [转帖]GCC 编译及编译选项
俗话说:'工欲善其事,必先利其器',一直在工作中使用GNU C编译器(以下简称GCC),这里对GCC的一些警告选项细致的分析,并列举几个简单的例子[注1]供分析参考. 1. -Wall集合警告选项我们 ...