Unity战斗单元UI血条架构设计

发布时间 2023-10-23 15:00:51作者: 游戏开发阿博

1:如何选取技术方案

 

Unity战斗系统中,每个角色经常会有血条与昵称。如何架构设计才高效,我们列举一些常用的做法:

(1) onGUI来做昵称与血条;

(2) 3D世界中创建一个3D物体来做血条与昵称,然后让血条与昵称对着摄像机;

(3) 基于UGUI/NGUI单独做血条与昵称的UI节点,然后把UI节点位置与角色同步;

(4) 其它可能的做法;

 

方案(1)中onGUI的性能是很差的, 一般只用于显示一些调试相关的信息,不适合用来做正式的游戏物体。方案(2)中创建3D物体,然后对着摄像机,能实现功能,可能的问题就是血条昵称这样的物体,可能会打乱物体的合批,使得drawcall比较高, 昵称与血条的Shader也可能与角色的不一样,会有不断切换Shader, set bass call爆增的风险。所以在正式的项目里面会使用方案(3)的UGUI/NGUI来单独创建血条与昵称的UI节点,然后角色移动同时更新同步UI节点的位置。

这样角色到哪里血条与昵称就跟到哪里。所有的血条与昵称都在UGUI的一个节点下,大量角色战斗的时候这些血条与昵称可以合批一起绘制,尽量降低Drawcall。方案定下来后,就基于这个思路来实现,需要解决角色与血条如何管理,角色与血条位置如何同步2大问题。

2: 如何排布角色节点与UI血条

 

3D角色美术建模完成以后,程序把它导入项目中,还要做一件很重要的事情,就是给这个角色加一个3D的节点mountPoint,这个节点在屏幕中的位置就是UI血条在屏幕中的位置,这个很关键。很多同学就会疑惑了,只要角色位置+一个固定的偏移就可以了,为什么要这个点呢?因为3D游戏与2D游戏不一样,3D游戏物体在屏幕中的位置除了和自己本身有关系外还和摄像机的角度有关系,所以不能直接是位置加一个偏移,而是在3D角色上加一个合适的3D的点,这个点在屏幕上的位置就是UI血条与昵称所在的位置,如图:

mountPoint的坐标转到屏幕坐标后,就是UI血条与昵称所在的屏幕位置。接下来看下UI节点。战斗场景中都会有一个操作UI,所有的UI都要盖在角色的血条与昵称上,所以当我们做战斗场景的主UI的时候,专门做一个UIBloodRoot节点,用来挂游戏中所有的血条与昵称对象,如图:

最后再做一个昵称+血条的UI对象,等角色创建的时候,再把这个对象创建出来,如图:

3 战斗系统中3D角色的UI代码设计

 

首先每个战斗单元,我们都会设计一个管理类提供策略,比如玩家对象,很多游戏会设计一个Player类来控制玩家, Boss怪会设计一个BossEnemy的类来控制Boss,而Player, Boss需要的很多策略,有些是共用的,血条就是其中之一,所以一般Player与Boss都会继承自一个策略基类FightCharactor,我们就可以把战斗掉血,吃道具加血以及UI血条的机制策略设计到FightCharactor里。

class FightCharactor extends Monobehaviour {

	// 实现掉血,加血,血条的机制;

	private float hp; // 战斗单元的血量

	private UICharactor uiCharactor; // 控制ui血条显示,角色昵称显示的组件实例;

	// end



	public void Init() {} //

}



Class Player extends FightCharactor {

	// 实现玩家特有的策略

	// end

}



Class BossEnemy extends FightCharactor {

	// 实现Boss特有的策略

	// end

}

接下来设计一个UICharactor组件类,new 一个组件类的实例添加到血条与昵称的ui对象上,来提供显示血条进度,与显示角色昵称的功能。

 

class UICharactor extends MonoBehaviour {

	public void SetBloodPer(float per) {…}

	public void SetUnick(string unick) {…}

}

 

在主战斗UI界面控制代码处提供一个接口UICharactor CreateUICharactor(),返回UIChractor对象实例。伪代码如下:

UICharactor CreateUICharactor() {

	GameObject uiObject = GameObject. Instantiate(UI血条+昵称的预制体);

	// 将血条对象放到UIBloodRoot节点下;

	uiObject.transform.SetParent(this.UIBloodRoot, false);



	UICharactor ctrl = uiObject.AddComponent<UICharactor>(); //添加组件实例到UI节点

	return ctrl;



}

在战斗角色FightCharactor类中的初始化方法Init中,调用UI控制代码的CreateUICharactor接口,获得UICharactor的组件实例,这样就可以在战斗掉血中,调用UICharactor的接口来显示血量与昵称了。

 

public void Init() {

	this.uiCharactor = 游戏UI界面控制对象实例. CreateUICharactor();

}

在代码中this.uiCharactor. SetBloodPer接口可以显示角色的血量,this.uiCharactor.SetUnick接口可以显示玩家的昵称等。

 

最后一件事情,就是角色到哪里,UI血条昵称对象的位置就更新到哪里,这个如何设计呢?可以在FightCharactor类里面写一个LateUpdate方法, 获取战斗单元中的mountPoint挂载点的位置, 然后将这个位置,结合游戏摄像机转换为屏幕坐标, 然后在UICharactor代码里编写一个函数ShowAt(Vector3 screenPos);在UGUI里面把屏幕坐标转成ui节点的世界坐标,这样UI血条昵称节点位置就放到mountPoint对应的屏幕点上了,玩家看起来就同步了。

class FightCharactor {

	// …

	void LateUpdate() {

		Vector3 worldPos = this.mountPoint.transform.position;

		Vector3 sceenPos = this.gameCamera.WorldToSceen(worldPos);

		this.uiCharactor.ShowAt(sceenPos);

	}

	// …

}



class UICharactor {

	// …

	public void ShowAt(Vector3 sceenPos) {

		Vector3 wordPos = ui模式下屏幕坐标转世界坐标;

		this.transform.position = worldPos;

	}

	// …

}

 

本期的战斗系统的UI血条昵称的架构与设计就分析到这里,我录制了了一个视频课件,完整的讲解与实现了刚才的架构与设计,关注我可以领取。