unity实战:狂暴机器人游戏教程(下篇)
Posted 肖尘
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了unity实战:狂暴机器人游戏教程(下篇)相关的知识,希望对你有一定的参考价值。
文章目录
前言
不了解或者看不懂的可以去看
unity实战:狂暴机器人游戏教程(上篇)
提示:以下是本篇文章正文内容,下面案例可供参考
三、敌人
3.1创建机器人
步骤1 创建脚本
单击“Resources”文件夹,然后将RedRobot、YellowRobot和BlueRobot拖到玩家面前的某个位置(位于地面之上)
如下图所示。
同时选中RedRobot、YellowRobot和BlueRobot游戏对象,在检视视图中单击Add Component按钮,然后选择New Script命令。命名脚本为Robot。这将在Assets文件夹中创建一个新脚本。将Robot脚本从Assets文件夹移动到Scripts文件夹。在代码编辑器中打开Robot脚本。添加以下变量。
[SerializeField]private string robotType;
public int health;
public int range;
public float fireRate;
public Transform missileFireSpot;
UnityEngine.AI.NavMeshAgent agent;
private Transform player;
private float timeLastFired;
private bool isDead;
robotType表示机器人的类型:RedRobot、BlueRobot或YellowRobot。
health是机器人生命值,
range是它能射击的距离,
fireRate 是它能射击的速度。
agent是对NavMeshAgent组件的引用,
player是机器人跟踪的对象,
isDead表示机器人是否死亡。
保存脚本并切换回Unity。单击RedRobot游戏对象,然后在 robotType字段中输入 RedRobot。将Health设置为 14,Range值为 150,Fire Rate为2。单击Apply按钮,将这些更改应用到预置。如下图所示。
对YellowBot执行相同的操作:Robot Type设为YellowRobot,Health设为20,Range设为300,Fire Rate设为3。
单击Apply按钮。
对BlueRobot执行相同的操作:Robot Type设为BlueRobot,Health设为10,Range设为200,Fire Rate设为1。
单击Apply按钮。
Missile Fire Spot参数是机器人发射导弹的位置。
单击RedRobot游戏对象旁边的箭头以显示其子对象。拖动其子游戏对象 MissileFireSpot 到Missile Fire Spot属性栏。
对YellowBot和BlueRobot执行同样的操作。如下图所示。
步骤2 定义生命周期
现在在代码编辑器中打开Robot脚本,修改代码。
void Start() {// 1
isDead = false;
agent = GetComponent<UnityEngine.AI.NavMeshAgent>();
player = GameObject.FindGameObjectWithTag("Player").transform;} // Update is called once per frame
void Update() {// 2
if (isDead) { return;}
// 3 transform.LookAt(player);
// 4 agent.SetDestination(player.position);
// 5 if (Vector3.Distance(transform.position,player.position) < range && Time.time - timeLastFired > fireRate) {
// 6 timeLastFired = Time.time; fire();}}
private void fire() {
Debug.Log("Fire");}
下面是对应编号代码中操作的解释:
①默认情况下,所有的机器人都是活的。然后将代理和player值分别设置为 NavMesh 代理和player组件。
②检查机器人是否已死。
③让机器人面对玩家。
④告诉机器人使用 NavMesh 找到玩家。
⑤检查玩家是否在射击范围内,并且是否攻击冷却完毕。
⑥更新timeLastFired到当前时间,并调用Fire方法,它只是暂时将消息记录到控制台。
接下来需要为玩家游戏对象分配一个合适的标签,以便于让机器人找到它。
保存脚本,回到Unity。点击Player游戏物体,点击标签下拉菜单,并选择Player标签。
运行游戏,将看到机器人向玩家移动,并且“Fire”日志不断出现在控制台中,如下图所示。
现在机器人在向玩家静态地移动。如果他们脚下的轮子滚起来的话,看起来会好得多。而且,机器人在向玩家开火时也应该有射击动作。
步骤1 添加足部动画
在随便哪个机器人上单击一个 RobotRampage_BotBall_v1 的游戏对象(展开其子对象上的箭头找到它),
然后转到Window\\Animation以打开Animation窗口。
单击Create按钮: 在名称处输入Ball,保存到Animations文件夹下,然后单击保存按钮。
单击AddProperty按钮,单击Transform旁边的箭头,然后单击Rotation旁边的+号,如图下所示。
一对关键帧(灰色菱形)将出现在时间轴上的1秒的位置。
单击最后一个关键帧,并注意到游戏对象的Rotation字段已在检视视图的转换组件中变为红色。将Rotation X值设置为-360。这将导致球旋转360度。
同时选择其他两个机器人的RobotRampage_BotBall_v1子游戏对象。选中所有RobotRampage_BotBall_v1游戏后,将RobotRampage_BotBall_v1拖到检视视图中。如下图所示。
运行游戏,会看到机器人脚下的球在旋转。
步骤3添加射击动画
全选三个RobotRampage_Bot游戏对象,并通过单击Controller字段旁边的圆圈选择Robot,如下图所示。
逐个选择每个RobotRampage_Bot,单击Prefab栏的Apply按钮,应用对预制体的修改,如下图所示。
现在打开Robot脚本。添加变量,然后修改Fire ()方法:
public Animator robot;
private void fire() { robot.Play("Fire");}
保存脚本,回到Unity。选择YellowRobot并将子物体RobotRampage_Bot拖入Robot属性。应用对预制体的修改。
如下图所示。
在RedRobot和BlueRobot上执行同样的操作。确保在完成操作后应用对预制体的更改。
运行游戏,看到机器人开火了
3.2发射机器人导弹
步骤1 创建导弹运动逻辑
新建一个C#脚本,命名为“Missile”。
将RobotMissileBlue、RobotMissileRed和RobotMissileYellow的预制体从项目视图的Resources文件夹拖到层级视图上。
保持选中层级视图中的RobotMissileBlue、RobotMissileRed和RobotMissileYellow游戏对象,然后将Missile脚本拖到检视面板。
在代码编辑器中打开Missile脚本并添加以下变量:
public float speed = 30f;
public int damage = 10;
speed是导弹飞行的快慢。
damage是导弹命中玩家时造成的伤害。
现在,在damage变量下面添加以下方法:
//1
void Start() {
StartCoroutine("deathTimer");}
// 2
void Update() {
transform.Translate(Vector3.forward * speed * Time.deltaTime);
}
// 3
IEnumerator deathTimer() {
yield return new WaitForSeconds(10); Destroy(gameObject);
}
在计算机术语中,会经常听到“线程”这个词。这是一种让计算机同时做多种事情的方法。在Unity中,可以使用coroutines(协程)来模拟线程。
协同方法返回IEnumerator。这些决定了协同的持续时间。下面是代码对应的注释:
①当实例化一个导弹,将开始一个名为“deathTimer”的协同方法。
②每帧向正前方移动相应的距离。
③该方法立即返回一个 WaitForSeconds,设置为10。意为该方法将在yield语句等待十秒后恢复。如果导弹没有击中球员,它将会自毁。
保存脚本,回到Unity。依次单击RobotMissileBlue、RobotMissileRed和RobotMissileYellow游戏对象上的Prefab栏的Apply按钮,应用对预制体的更改。
步骤2 添加导弹发射方法
打开Robot脚本。在该文件的顶部,在类的“{”下面添加以下内容:
[SerializeField] GameObject missileprefab;
missilePrefab 是导弹的预制体。修改Fire方法,代码如下。
private void fire() {
GameObject missile = Instantiate(missileprefab); missile.transform.position=missileFireSpot.transform.position; missile.transform.rotation=missileFireSpot.transform.rotation; robot.Play("Fire");
}
这将实例化一个新的 missilePrefab,并设置其位置和旋转到机器人的射击点。保存脚本回到Unity。
从层级视图中删除RobotMissileBlue、RobotMissileRed和RobotMissileYellow游戏对象。
单击RedRobot游戏对象,将RobotMissileRed预置体从Resources文件夹拖到Robot组件的Missile prefab栏中,然后单击Apply按钮。如图3-3-1所示。
在YellowRobot和BlueRobot游戏对象上执行同样的操作,确保最后单击了Apply按钮。
运行游戏,看到机器人发射导弹
3.3增加伤害
步骤1 添加玩家受击方法
导弹会伤害到玩家,玩家也会损坏机器人。新建一个新的C#脚本,命名它Player并添加以下变量:
public int health;
public int armor;
public GameUI gameUI;
private GunEquipper gunEquipper;
private Ammo ammo;
health是玩家的剩余生命值。当生命值为零,游戏结束。
armor是一个玩家的装甲,减少50%的伤害。
一旦装甲变成0,玩家将受到100%伤害。
gameUI和gunEquipper是对脚本的引用。
ammo是提前建立的弹药类。将以下内容添加到Start方法:
void Start () {
ammo = GetComponent<Ammo>();
gunEquipper = GetComponent<GunEquipper>();
}
这只是初始化对Ammo和GunEquipper组件的引用。添加以下方法:
public void TakeDamage(int amount) {
int healthDamage = amount;
if (armor > 0) {
int effectiveArmor = armor * 2; effectiveArmor -= healthDamage;
if (effectiveArmor > 0) {
armor = effectiveArmor / 2;
return;}
armor = 0;
}health -= healthDamage;
Debug.Log("Health is " + health);
if (health <= 0) { Debug.Log("GameOver");}}
TakeDamage ()根据玩家剩余的装甲量来计算伤害减免。
如果玩家没有盔甲,那么将会受到完整伤害。
如果生命值达到0,游戏将会结束;
现在,只需要将其记录到控制台。
保存脚本,回到Unity。
将Player脚本添加到Player游戏对象。
设置Health为100,Armor为 20,
并拖动GameUI游戏对象到GameUI字段。
步骤2 导弹检测碰撞
当导弹与玩家的碰撞器相撞,它将通过OnCollisionEnter()处理伤害玩家的计算。在代码编辑器中打开Missile脚本并添加以下方法:
void OnCollisionEnter(Collision collider) {
if(collider.gameObject.GetComponent<Player>()!=null&&collider.gameObject.tag == "Player") {
collider.gameObject.GetComponent<Player>().TakeDamage(damage);
}
Destroy(gameObject);
}
导弹通过检查与它碰撞的游戏对象Tag是否为Player来判断是否击中玩家。
它还要检查是否Player脚本处于激活状态,因为Player组件将在游戏结束后被禁用。
如果两个条件都满足,它将通过调用Player脚本上的TakeDamage方法来计算伤害。
导弹还会同时销毁自身。
保存脚本,回到Unity。
运行游戏。让炮弹击中自己,查看控制台播放的玩家受击信息
步骤3 添加机器人受击方法
当玩家的生命值到0时,记录游戏结束。稍后,将会实现更好的处理方法。
目前机器人仍然是无敌的。因为它们还没有添加受击方法。打开Robot脚本并添加以下方法:
// 1
public void TakeDamage(int amount) {
if (isDead) {return;}
health -= amount;
if (health <= 0)
{isDead = true;
robot.Play("Die");
StartCoroutine("DestroyRobot");}}
// 2
IEnumerator DestroyRobot() {
yield return new WaitForSeconds(1.5f);
Destroy(gameObject);}
①这与玩家TakeDamage()方法的逻辑大致相同。唯一不同的地方是,机器人将在生命值为0的时候先播放死亡动画再调用DestroyRobot ()销毁自身。
②这在摧毁机器人之前增加了一个1.5秒的延迟,为Die动画播放完成提供了足够的时间。
保存脚本。
步骤4 实现聚焦
不同的枪支应该有不同的攻击范围、破坏力以及通过鼠标右键可以实现不同程度的聚焦。
打开Gun脚本,并添加以下变量:
public float zoomFactor;
public int range;
public int damage;
private float zoomFOV;
private float zoomSpeed = 6;
当玩家点击鼠标右键时,zoomFactor控制缩放级别。
zoomFOV是最终缩放视角。
range是枪的射程。猎枪的射程最短,而手枪最长。
damage是枪造成的伤害。
保存脚本回到Unity。
单击1Pistol游戏对象,并将其ZoomFactor设置为1.3,Range为60,Damage为3。
单击2AssaultRifle游戏对象,并将其ZoomFactor设置为1.4,Range为30,Damage为1。
单击3Shotgun游戏对象,并将其ZoomFactor设置为1.1,Range为10,Damage为10。
打开Gun脚本并更新Start ()方法到以下内容:
void Start() {
zoomFOV = Constants.CameraDefaultZoom / zoomFactor;
lastFireTime = Time.time - 10;}
这只是初始化了缩放因子。修改Update()方法,代码如下。
protected virtual void Update() {
// Right Click (Zoom)
if (Input.GetMouseButton(1)) {
Camera.main.fieldOfView=Mathf.Lerp(Camera.main.fieldOfView,zoomFOV,zoomSpeed * Time.deltaTime);
} else {
Camera.main.fieldOfView = Constants.CameraDefaultZoom;}}
如果玩家点击鼠标右键,这将通过 Mathf Lerp平滑地播放缩放效果。
步骤5 添加射线
为了确定机器人是否被击中,将使用光线。光线是一个隐形的射线,但是可以检测到碰撞。首先,必须定义命中的方法。添加以下内容:
private void processHit(GameObject hitObject) {
if (hitObject.GetComponent<Player>() != null) {hitObject.GetComponent<Player>().TakeDamage(damage);}
if(hitObject.GetComponent<Robot>()!=null){ hitObject.GetComponent<Robot>().TakeDamage(damage);}}
这个方法将伤害值传递给正确的游戏对象。为了实现光线,将以下内容添加到Fire方法的底部。
Ray ray = Camera.main.ViewportPointToRay(new Vector3(0.5f,0.5f,0));
RaycastHit hit;
if (Physics.Raycast(ray,out hit,range)) { processHit(hit.collider.gameObject);}
这将创建一个射线,并检查射线的命中。道理很简单,如果射线在枪的射程内与一个游戏对象碰撞就会触发processHit方法。
processHit判断命中的是否是机器人,如果是,机器人将会受到伤害。
保存脚本,回到Unity。运行游戏,按住鼠标右键缩放,然后攻击那些机器人,直到他们死掉。
3.4创建补给
步骤1 添加拾取补给的方法
玩家需要一种方法来恢复生命值、护甲和弹药。案例将在场景的随机位置添加一些漂浮的补给物让玩家拾取。在代码编辑器中打开Player脚本,并添加以下内容:
// 1
private void pickupHealth() {
health += 50;
if (health > 200) {health = 200;}}
private void pickupArmor() { armor += 15;}
// 2
private void pickupAssaultRifleAmmo() { ammo.AddAmmo(Constants.AssaultRifle,50);}
private void pickupPisolAmmo() { ammo.AddAmmo(Constants.Pistol,20);} private void pickupShotgunAmmo() { ammo.AddAmmo(Constants.Shotgun,10);}
这些方法实现了玩家捡到补给物时获得的增益效果。
现在添加以下内容:
public void PickUpItem(int pickupType) {
switch (pickupType) {
case Constants.PickUpArmor:
pickupArmor();break;
case Constants.PickUpHealth:
pickupHealth();break;
case Constants.PickUpAssaultRifleAmmo: pickupAssaultRifleAmmo();break;
case Constants.PickUpPistolAmmo:
pickupPisolAmmo();break;
case Constants.PickUpShotgunAmmo:
pickupShotgunAmmo();break; default:
Debug.LogError("Bad pickup type passed" + pickupType); break;}}
PickUpItem方法通过传入int参数,获取正在拾取的补给物的类型。
步骤2 创建补给逻辑
Constants文件引用所有补给物类型的ID。这些ID对应了五种类型的补给物。这些ID将作为参数传递到PickUpItem 方法中。
保存脚本,回到Unity。新建一个C#脚本,命名为“Pickup”,并添加字段,代码如下。
public int type;
这表示补给物的类型。现在添加以下内容
void OnTriggerEnter(Collider collider) {
if(collider.gameObject.GetComponent<Player>()!=null&&collider.gameObject.tag == "Player") {
collider.gameObject.GetComponent<Player>().PickUpItem(type);Destroy(gameObject);
}}
这会让补给物监听与玩家的碰撞,从而调用玩家身上Player脚本上的PickUpItem方法,并传入自身的补给类型,然后销毁自身。
保存脚本,回到Unity。
在Resources文件夹中,选择PickupAmmoAssaultRifle、PickupAmmoPistol、PickupAmmoShotgun、PickupHealth和PickupArmor预设。选中所有这些选项后,单击脚本类别中的Add Component按钮,选择Pickup脚本。
接下来,添加Rigidbody。勾选Is Kinematic。
最后,添加BoxCollider。勾选Is Trigger,并设置Size为(1.5、1.5、1.5)。如下图所示
现在来配置每个补给物的类型。
选择Resources文件夹中的PickupAmmoPistol,
并将其Type设置为1,
将PickupAmmoAssaultRifle设置为2,
PickupAmmoShotgun为3,
PickupHealth为4,
PickupArmor为5。
步骤3 添加补给的漂浮动画
现在要做的事情是再补给物上添加旋转和浮动的动画。动画已经预先创建好了,现在只是需要创建Animator。在项目视图的Animations文件夹中创建一个Animator Controller,命名为“Pickup”。
双击Pickup动画控制器打开Animator视图。在空网格处右击唤出快捷菜单,然后选择Create State\\Empty命令。在检视视图中,更名为“Spin”并将Motion设置为cubeSpin,如下图所示。
接下来,返回到项目视图并选择Resources文件夹。
展开PickupAmmoAssaultRifle、PickupAmmoPistol、PickupAmmoShotgun、PickupHealth和PickupArmor预设,
选中它们的子物体。
将项目视图中Animations文件夹里的Pickup拖到检视视图。
现在,将这五个补给物(不是子物体)复制到场景上,位置略高于地面,如下图所示。
运行游戏。控制玩家移动到每个补给上。
当主角接触到补给的时候,会获得增益,并且补给物会消失。
如图所示。下一章将会添加一个UI来查看这些增益效果。
3.5补给刷新点生成
步骤1 创建补给点
在层级视图中删除所有的Pickup和Robot游戏对象。
创建一个新的C#脚本,将其命名为“PickupSpawn”,并添加字段。
private GameObject[] pickups;
pickups将保存所有补给类型。现在添加以下方法:
// 1void spawnPickup() {// Instantiate a random pickup
GameObject pickup = Instantiate(pickups[Random.Range(0,pickups.Length)]);
pickup.transform.position = transform.position; pickup.transform.parent = transform;}
// 2IEnumerator respawnPickup() {
yield return new WaitForSeconds(20); spawnPickup();}
// 3void Start() { spawnPickup();}
// 4public void PickupWasPickedUp() { StartCoroutine("respawnPickup");}
下面是每个方法的工作:
①实例化一个随机类型的补给,并设置它的位置。
②在调用spawnPickup方法之前等待20秒。
③在自身实例化时生成一个补给。
④当玩家捡起补给时,重置协同方法。
保存脚本。打开Pickup脚本,然后在Destroy(gameObject)上添加以下行:
GetComponentInParent<PickupSpawn>().PickupWasPickedUp();
当补给与主角相撞时,将在PickupSpawn脚本上启动生成计时器。
保存脚本回到Unity。
创建一个空的游戏对象,命名为“PickupSpawn”,并添加PickupSpawn脚本。
选中PickupSpawn的游戏对象,设置其Pickups属性的Size为5。
Lock检视视图。将Resources文件夹中的PickupAmmoAssaultRifle、PickupAmmoPistol、PickupAmmoShotgun、PickupHealth和 PickupArmor预设分别拖入到Pickups元素中。
最后,UnLock检视视图。
步骤2 设置所有补给点
现在,将PickupSpawn游戏对象拖到Resources文件夹中,创建一个预制体。
创建一个空的游戏对象,命名为“PickupSpawns”。将其Position设置为 (0、0、0)。将PickupSpawn拖入到PickupSpawns,使其成为PickupSpawns的子物体。然后再复制出六个。
为了保持整洁,将每个子对象名称都更改为“PickupSpawn”。
如下图所示。
从最顶部PickupSpawn的游戏对象开始设置位置:
运行游戏,会看到七个补给生成在场景中。走过去,把它们捡起来。
步骤3 创建传送点
现在,补给已经可以自动生成了,下面来实现机器人的自动生成。
将Teleporter预置从Resources文件夹拖到场景的根目录下。
创建一个C#脚本,将其命名为“RobotSpawn”。
在代码编辑器中打开它,并在类的“{”之后添加以下变量:
[SerializeField] GameObject[] robots;
private int timesSpawned;
private int healthBonus = 0;
Robots保存了将要用来实例化的所有类型的机器人预制体。
healthBonus 是每个机器人每波获得多少生命值,当玩家活得越久,游戏将变得越难。
timesSpawned 是机器人的生成周期。
接下来添加以下方法:
public void SpawnRobot() {
timesSpawned++;
healthBonus += 1 * timesSpawned;
GameObject robot = Instantiate(robots[Random.Range(0,robots.Length)]);
robot.transform.position=transform.position;
robot.GetComponent<Robot>().health += healthBonus;
}
SpawnRobot ()方法用来实例化一个机器人,并设置它的生命值和位置。
保存脚本回到Unity。
将RobotSpawn脚本拖到Teleporter游戏对象上。在层级视图中选择Teleporter游戏对象,然后将RedRobot、YellowRobot和BlueRobot预制体从Resource文件夹中拖到检视视图的Robots字段中。
unity实战:狂暴机器人游戏教程(下篇)
游戏开发实战用Go语言写一个服务器,实现与Unity客户端通信(Golang | Unity | Socket | 通信 | 教程 | 附工程源码)