Player Health (Single Player)
球员的健康(单人)
Synchronizing Player States is an important concept in Multiplayer Networking.
同步播放器状态是多人网络中的一个重要概念。
As an example, we will have the bullets do damage by affecting a Health property of a player when the bullets collide them.
举个例子,当子弹碰撞时,子弹会影响运动员的健康属性,从而造成伤害。
The Current Health of the player will be a state and we will synchronize the state of the player’s current health across the network.
玩家当前的健康状态将是一个状态,我们将同步玩家当前健康状态的状态。
Create Bullet Collisions
创建子弹的碰撞
The first step to having the bullet affect its target is to add collision handling logic to the bullet.
让子弹影响目标的第一步是将冲突处理逻辑添加到子弹中。
For this example, we will simply destroy the bullet GameObject when it hits any other collider.
对于这个例子,我们将简单地摧毁子弹的游戏对象,当它击中任何其他的碰撞器。
Create and Add a new script named “Bullet ” to the Bullet prefab asset.
创建并添加一个名为“Bullet”的新脚本,该脚本可用于“子弹预置”资产。
Open the Bullet script for editing.
打开用于编辑的脚本。
Remove all of the sample code in the script.
删除脚本中的所有示例代码。
Add collision handling logic that destroys the bullet when it comes in contact with another collider.
添加碰撞处理逻辑,当它与另一个碰撞器接触时,会破坏子弹。
using UnityEngine;
using System.Collections;
public class Bullet : MonoBehaviour
{
void OnCollisionEnter()
{
Destroy(gameObject);
}
}
Save the Bullet Script.
节省子弹的脚本。
Return to Unity.
回到Unity。
Test the current state of the project.
测试项目的当前状态。
Build and Run this scene as a standalone application.
构建并运行这个场景作为一个独立的应用程序。
Click the Host button from the in-game UI to start this game as a Host.
单击游戏内UI中的主机按钮以作为主机启动此游戏。
Move the player GameObject.
玩家GameObject移动。
Return to Unity.
回到Unity。
Enter Play Mode.
进入播放模式。
Click the LAN Client button from the in-game UI to connect to the Host as a Client.
单击游戏内UI中的LAN客户端按钮以连接到主机作为客户端。
Now when a bullet hits another GameObject with a Collider attached, the bullet will be destroyed.
现在当一颗子弹击中另一个装有碰撞器的游戏物体时,子弹就会被摧毁。
Because the bullet is being managed by the NetworkManager, when the bullet on the Server is destroyed, it will be destroyed on all of the Clients as well.
因为这颗子弹是由NetworkManager管理的,当服务器上的子弹被摧毁时,它也会在所有的客户端被销毁。
Close the standalone player.
关闭独立的球员。
Return to Unity.
回到Unity。
Exit Play Mode.
退出播放模式。
Player Health
运动员的健康
To create a single player version of Health, we will need a new script to track and display the Current Health of our player.
为了创建一个单一的播放器版本的健康,我们需要一个新的脚本来跟踪和显示我们的球员的当前健康状况。
Create and Add a new script named “Health” to the Player prefab asset.
创建并添加一个名为“Health”的新脚本给播放器预置资产。
Open the Health script for editing.
打开用于编辑的健康脚本。
Remove all of the sample code in the script.
删除脚本中的所有示例代码。
Add a field to hold the constant starting value of, or maximum, health;
添加一个字段来保持或最大限度地保持健康的起始值;
initialized to 100.
初始化到100。
public const int maxHealth = 100;
Add a field to hold the Current Health; initialized to Max Health.
添加一个字段来最大初始化
public int currentHealth = maxHealth;
Add logic to handle taking damage and reducing health when player is hit.
当玩家被击中时,增加伤害和减少血量。
public void TakeDamage(int amount)
{
currentHealth -= amount;
if (currentHealth <= 0)
{
currentHealth = 0;
Debug.Log("Dead!");
}
}
The final Health script should look like this.
Health
C#
using UnityEngine;
public class Health : MonoBehaviour
{
public const int maxHealth = 100;
public int currentHealth = maxHealth;
public void TakeDamage(int amount)
{
currentHealth -= amount;
if (currentHealth <= 0)
{
currentHealth = 0;
Debug.Log("Dead!");
}
}
}
Save the Health script.
保存脚本。
The Bullet script will need to be updated to call the TakeDamage function on target player’s Health script when it hits the target player.
当它击中目标玩家时,子弹脚本需要更新为目标玩家的Health脚本上的TakeDamage功能。
Open the Bullet script for editing.
打开用于编辑的脚本。
Change the OnCollisionEnter function to have a Collision parameter.
将OnCollisionEnter函数更改为具有冲突参数。
void OnCollisionEnter(Collision collision)
Add a call to TakeDamage from the Health script in the collision handling function.
var hit = collision.gameObject;
var health = hit.GetComponent();
if (health != null)
{
health.TakeDamage(10);
}
The final Bullet script should look like this:
Bullet
C#
using UnityEngine;
using System.Collections;
public class Bullet : MonoBehaviour
{
void OnCollisionEnter(Collision collision)
{
var hit = collision.gameObject; var health = hit.GetComponent();
if (health != null)
{
health.TakeDamage(10);
}
Destroy(gameObject);
}
}
Save the Bullet script.
节省子弹的脚本。
The bullet will now damage the target player.
子弹现在会伤害目标玩家。
Damage to the player, however, is not visible in the scene.
然而,对玩家的伤害在现场是不可见的。
We will need to create a simple health bar for each player.
我们需要为每个玩家创建一个简单的健康栏。
Creating A Simple Healthbar
创建一个简单的Healthbar
To do this, we will need to create a simple set of UI elements.
为此,我们需要创建一个简单的UI元素集。
It is worth noting that the following solution is less than optimal for a final production quality game, but for the sake of this example, this solution will address the immediate problem of needing a simple healthbar in a few easy steps.
值得注意的是,下面的解决方案对于最终的产品质量游戏来说并不是最理想的,但是为了这个例子,这个解决方案将解决需要一个简单的healthbar的几个简单步骤的迫切问题。
Create a new UI Image in the scene.
在场景中创建一个新的UI映像。
Note that this will also create a new parent Canvas and an EventSystem GameObject.
注意,这还将创建一个新的父画布和一个EventSystem游戏对象。
Rename the Canvas GameObject to “Healthbar Canvas”.
将画布游戏对象重命名为“Healthbar Canvas”。
Rename the Image GameObject to “Background”.
将图像游戏对象重命名为“Background”。
With the Background GameObject selected:
选择的背景游戏对象:
…
set the RectTransform component’s Width property to 100.
将RectTransform组件的宽度属性设置为100。
…
set the RectTransform component’s Height property to 10.
将RectTransform组件的高度属性设置为10。
…
set the Image component’s Source Image to the built-in InputFieldBackground.
将图像组件的源映像设置为内置InputFieldBackground。
…
set the Image component’s Color to Red.
将图像组件的颜色设置为红色。
Do not change the Anchor or Pivot in the Anchor Presets Window.
不要在锚预置窗口中改变锚或锚点。
Duplicate the Background GameObject.
复制背景GameObject。
Rename the duplicated Background GameObject to “Foreground”.
将复制的背景游戏对象重命名为“前景”。
Make the Foreground GameObject a Child of the Background GameObject.
让前景的游戏对象成为背景游戏对象的孩子。
The organization of the Healthbar should look like this:
Healthbar的组织应该是这样的:
With the Foreground GameObject selected:
选择了前景的GameObject:
...
…
Set the Image component’s Color to Green.
将图像组件的颜色设置为绿色。
...
…
Open the Anchor Presets Window and Set the Pivot and Position to Middle Left.
打开锚预置窗口,并将枢轴和位置设置为中左。
This Healthbar needs to be added to the player prefab and hooked into the current health and damage system.
这个Healthbar需要添加到玩家预置,并连接到当前的健康和伤害系统。
First, the Canvas needs to be changed from a default Overlay Canvas into a World Space Canvas and then it needs to be added to the player prefab.
首先,画布需要从默认的覆盖画布更改为世界空间画布,然后需要添加到播放器预置中。
For more information on Canvases and how they work, please see the page on Canvas.
有关画布的更多信息以及它们的工作方式,请参阅画布上的页面。
Drag the Player prefab from the Project Window into the Hierarchy to create an instance in the scene.
将播放器预置从项目窗口拖到层次结构中,在场景中创建一个实例。
With the Healthbar Canvas selected:
选择了Healthbar画布:
…
change the Canvas’ Render Mode to World Space.
将画布的渲染模式更改为世界空间。
Make the Healthbar Canvas a Child of the Player GameObject.
让Healthbar画布成为玩家游戏对象的一个孩子。
The organization of the Player should look like this:
球员的组织应该是这样的:
With the Healthbar Canvas selected:
选择了Healthbar画布:
...
…
reset the RectTransform component with the gear menu.
使用gear菜单重置RectTransform组件。
...
…
set the RectTransform’s Scale to (0.01, 0.01, 0.01).
将RectTransform的比例设置为(0.01,0.01,0.01)。
...
…
set the RectTransform’s Position to (0.0, 1.5, 0.0).
将RectTransform的位置设置为(0.0,1.5,0.0)。
With the Player GameObject selected:
选择了玩家的游戏对象:
...
…
apply the changes to the Player prefab.
将更改应用于播放器预置。
Save the scene.
保存场景。
To hook the Healthbar Canvas into the current health and damage system, we will need to reference the Healthbar from the Health script and set the Foreground Image to display the Current Health.
为了将Healthbar Canvas连接到当前的健康和损害系统中,我们需要从健康脚本中引用Healthbar,并设置前景图像来显示当前的健康状况。
Open the Health script for editing.
打开用于编辑的健康脚本。
Add the namespace UnityEngine.UI.
UnityEngine.UI添加名称空间。
using UnityEngine.UI;
Add a field to hold the reference to the RectTranform on the Healthbar’s Foreground element.
public RectTransform healthBar;
Note that we are referencing the Foreground element’s RectTransform here as we will be changing the *RectTransform’s Width/ property to change the Healthbar’s display.
注意,我们正在引用前台元素的RectTransform,因为我们将更改*RectTransform的宽度/属性以更改Healthbar的显示。
Add logic to update the Healthbar’s display to reflect the Current Health value.
healthBar.sizeDelta = new Vector2(
currentHealth,
healthBar.sizeDelta.y);
Note that we need to change the RectTransform’s Size Delta as a Vector2 to change the width.
请注意,我们需要将RectTransform的大小Delta改为Vector2,以改变宽度。
The final script should look like this:
最后的脚本应该是这样的:
Health
C#
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class Health : MonoBehaviour {
public const int maxHealth = 100;
public int currentHealth = maxHealth;
public RectTransform healthBar;
public void TakeDamage(int amount)
{
currentHealth -= amount;
if (currentHealth <= 0)
{
currentHealth = 0;
Debug.Log("Dead!");
}
healthBar.sizeDelta = new Vector2(currentHealth, healthBar.sizeDelta.y);
}
}
Save the script.
保存脚本。
Return to Unity.
回到团结。
With the Player GameObject selected in the Hierarchy Window:
在层次结构窗口中选择的玩家游戏对象:
...
…
set the Healthbar Field on the Health component to the Foreground GameObject.
将健康组件的Healthbar字段设置为前台GameObject。
With the Player GameObject selected:
选择了玩家的游戏对象:
...
…
apply the changes to the Player prefab.
将更改应用于播放器预置。
Delete the instance of the Player GameObject from scene.
从场景中删除玩家游戏对象的实例。
Save the scene.
保存场景。
Lastly, we will need to keep the Healthbar facing the main camera.
最后,我们需要保持对主摄像头的健康栏。
With the Healthbar Canvas on the Player prefab selected in the Project window:
在项目窗口中选择的球员预制板上的Healthbar Canvas:
...
…
create and add a new script called “Billboard”.
创建并添加一个名为“公告牌”的新脚本。
Open the Billboard script for editing.
打开Billboard脚本进行编辑。
Add logic in the Update function to keep the Healthbar looking at the Main Camera.
在更新函数中添加逻辑以保持Healthbar查看主摄像头。
transform.LookAt(Camera.main.transform);
Remove all unnecessary sample code.
删除所有不必要的示例代码。
The final script should look like this:
最后的脚本应该是这样的:
Billboard
C#
using UnityEngine;
using System.Collections;
public class Billboard : MonoBehaviour {
void Update () {
transform.LookAt(Camera.main.transform);
}
}
Test these additions.
这些添加测试。
Build and Run this scene as a standalone application.
构建并运行这个场景作为一个独立的应用程序。
Click the Host button from the in-game UI to start this game as a Host.
单击游戏内UI中的主机按钮以作为主机启动此游戏。
Move the player GameObject.
玩家GameObject移动。
Return to Unity.
回到Unity。
Enter Play Mode.
进入播放模式。
Click the LAN Client button from the in-game UI to connect to the Host as a Client.
单击游戏内UI中的LAN客户端按钮以连接到主机作为客户端。
Currently, changes to player's current health are being applied independently on all of the Clients and the Server.
目前,对玩家当前健康状况的改变正在所有客户端和服务器上独立应用。
When one player shoots another, the Bullet and Health scripts are working "locally” on each particular Client and the Server.
当一个玩家射杀另一个玩家时,子弹和健康脚本在每个特定的客户端和服务器上“本地”工作。
There is no synchronization happening.
没有同步发生。
The bullets, however, are being managed as Spawned objects through the NetworkManager.
然而,通过网络管理器,可以将这些子弹作为衍生对象来管理。
They will be destroyed on all Clients when they have detected a collision.
当他们发现碰撞时,所有的客户都将被销毁。
Because the bullets exists on every Client, it is possible to have collisions between the bullet and a player, and it is possible that the player could take damage.
因为子弹存在于每个客户端,所以在子弹和玩家之间发生碰撞是可能的,玩家可能会受到伤害。
It is also possible, however, due to latency and other issues, that the bullet could get destroyed on one Client before a collision is registered on another.
但是,由于延迟和其他问题,在另一个客户端发生碰撞之前,子弹可能会在一个客户端被销毁。
Because the bullets are being synchronized, but the player’s Current Health is not, this can lead to the player’s Current Health being completely different between all of the Clients and the Server.
因为子弹是同步的,但玩家当前的健康状况并不是这样,这可能导致玩家当前的健康状况在所有客户端和服务器之间完全不同。
At this point if the two players shoot at each other often enough, it should be easy to see the player’s current health differ between Clients.
在这一点上,如果两个球员经常互相射击,很容易看出球员的当前健康状况不同。
Close the standalone player.
关闭独立的球员。
Return to Unity.
回到Unity。
Exit Play Mode.
退出播放模式。