【Unity俯视角射击】我们来做一个《元气骑士》的完整Demo(四)

作者:Yumir
哈喽大家好,我是yumir。
惯例先上成果链接:https://connect.unity.com/mg/other/untitled-9864
本来本篇应该是完结篇,主要是补充一下被攻击方法和房间管理系统这两个在游戏中占比不低的功能块,稍微有点复杂。
但是写的时候发现要写的内容篇幅实在是太长了,所以还是把两个内容分开写了,不过我的游戏资源已经打包好了就先放出来吧,你们应该不会因为这样就不追更了吧不会吧不会吧。
一、被攻击方法
为什么我要单独拎这一个方法出来讲呢?
因为被攻击既有玩家攻击怪物又有怪物攻击玩家,未来场景中有许多可以被攻击的物体也有许多不能被攻击的物体。
所以假设子弹碰撞到物体的时候,物体可能具有某个特殊的性质,也有可能没有,而我需要一个方便快捷的方法。
我编写了一个BeAttack接口:
interface BeAttack
{
void BeAttack(float data);
}
接着我让所有可以“被攻击”的游戏物体都继承这个接口。
当子弹的碰撞体碰撞到了物体,通过GetComponent的结果是否为空,可以判断碰到的物体能不能被攻击,如果能则运行相应的方法,反之则说明这个物体不是可以被攻击的物体:
if (collision.GetComponent<BeAttack>() != null){
collision.GetComponent<BeAttack>().BeAttack(attack);
Die();
}
else if (collision.transform.tag == "Wall"){
Die();
}
二、玩家的被攻击
继承了BeAttack的游戏物体需要编写相应的方法逻辑,数据处理和画面表现加起来量还是很大的。
设定中玩家是具有生命值和防御值的,所以在玩家被攻击时需要分别判断数值减去对应的数据,并且在玩家最后一次被攻击之后一段时间开始恢复玩家的防御值,也就是脱离战场之后恢复。
//脱离战场&&防御值需要恢复&&恢复CD完成
if (Time.time - dpTiming1 > dpCD1 && dpRestore && Time.time - dpTiming2 > dpCD2){
dpTiming2 = Time.time;
playerDP.realValue++;
if (playerDP.realValue >= playerDP.maxValue){
playerDP.realValue = playerDP.maxValue;
dpRestore = false;
}
}
这里我用了一个布尔值dpRestore作为标记,两个浮点数作为计时器,一个是脱离战场计时,一个是恢复防御值CD。
public void BeAttack(float data){
dpRestore = true;
dpTiming1 = Time.time;
if (playerDP.realValue == 0){
playerHP.realValue -= data;
}
else if (playerDP.realValue < data){
playerHP.realValue -= data - playerMP.realValue;
playerDP.realValue = 0;
}
else{
playerDP.realValue -= data;
}
if (playerHP.realValue <= 0){
playerAnima.SetBool("die", true);
isDie = true;
GameManager.instance.GameOver();
GetComponent<Collider2D>().enabled = false;
}
else{
playerAnima.Play("BeAttack");
GetComponent<CinemachineImpulseSource>().GenerateImpulse();
GameManager.instance.ShowAttack(data, Camera.main.WorldToScreenPoint(transform.position + new Vector3(0, 1, 0)));
}
}
Over方法,并且将玩家的碰撞体取消激活。
除此之外,当玩家被攻击后血量大于零,则需要播放被攻击的动画,并且让镜头抖动。
在添加Cinemachine的游戏物体“CM vcam1”上点击AddExtension右边的下拉菜单,选择Cinemachine Impulse Listener,会出现下图的组件,注意要勾选Use 2D Distance。

再在Player游戏物体上添加Cinemachine Impulse Source组件,设置方面因为我也不懂抖动的艺术所以直接选择了一个默认的Raw Signal,你可以试着自己调一个。

最后,除了被攻击的动画和镜头抖动这两个效果,还需要一个UI上面的数值显示,为此我制作了一个用于显示被攻击的UI预制体。
①新建一个Text,调整成合适的大小和颜色,并新建一个脚本用于控制该UI。
②编写脚本,该预制体的脚本逻辑非常简单,用DoTween做了一段动画,结束时删除自己即可。
public class BeAttackText : MonoBehaviour
{
void Start()
{
transform.localScale = new Vector3(0, 0, 1);
transform.DOScale(new Vector3(1, 1, 0), 0.4f);
transform.DOMoveY(transform.position.y + 20, 0.5f);
Destroy(gameObject,0.6f);
}
}
③在GameManager中控制在对应的位置生成该预制体。
public void ShowAttack(float data, Vector3 pos){
GameObject textgo = Instantiate(beAttackText, pos, Quaternion.identity);
textgo.transform.SetParent(GameObject.Find("Canvas").transform);
textgo.GetComponent<Text>().text = "-" + data;
}
在BeAttack方法中调用这个ShowAttack方法,传入屏幕映射值作为生成该预制体的位置。
三、怪物的被攻击
虽然游戏中有四种怪,但是被攻击时目前来讲没有什么差别,主要操作都是在父类里面写的。
public virtual void BeAttack(float data){
hp -= data;
if (hp <= 0){
monsterState = MonsterState.Die;
GetComponent<Animator>().SetBool("die", true);
GetComponent<Collider2D>().enabled = false;
room.MonsterDie(this);
for (int i = 0; i < coin; i++){
Instantiate(GameManager.instance.coinPre, transform.position, Quaternion.identity);
}
for (int i = 0; i < magic; i++){
Instantiate(GameManager.instance.mpPre, transform.position, Quaternion.identity);
}
}
else{
GetComponent<Animator>().Play("BeAttack");
GameManager.instance.ShowAttack(data, Camera.main.WorldToScreenPoint(transform.position + new Vector3(0, 1, 0)));
}
}
基本结构和玩家被攻击方法也差不多,只是没有防御值判断。
其中不同的地方一个是怪物死亡之后需要通知自己所在的房间自己已经被打败了,在房间持有怪物List中删除自己。
另一个就是需要生成金币和魔法石,而这些金币和魔法石在玩家靠近时会自动飞向玩家。

private void Update(){
if (!isDrop){
if (Vector3.Distance(transform.position, target) > 0.01)
transform.position = Vector3.Lerp(transform.position, target, Time.deltaTime * speed);
else
isDrop = true;
}
else{
if (Vector3.Distance(transform.position, player.position) <= getDistance)
transform.position = Vector3.Lerp(transform.position, player.position, Time.deltaTime * speed);
if (Vector3.Distance(transform.position, player.position) <= 0.5){
player.GetComponent<PlayerControl>().AddCoin();
Destroy(gameObject);
}
}
}

除此之外还可以制作一些木箱子花瓶之类的可以被打碎的道具,或者是打碎之后有物体溢出的箱子等等。
你可以设想一下如果不使用接口应该如何实现这些逻辑判断,或许你会想到更好的办法?
游戏Demo源码+素材网盘链接:https://pan.baidu.com/share/init?surl=FUGcRoh3Zw9SwKXe67DZiQ
提取码:8bwo

欢迎加入游戏开发群欢乐搅基:1082025059
对游戏开发感兴趣的童鞋可戳这里进一步了解:http://www.levelpp.com/