前言
大家好,上一篇文章我设计了一个简单的第一个BOSS,以及二个新敌人。然后这个文章准备设计第二个BOSS,虽然感觉做这些挺水的,但是作为一个的大学生,也就只可以这样的,毕竟难的我也确实不会,先写着练练手吧。然后,今天的代码比之前少了一点,但是写的时候还是吃了些力,可能还是因为技术不够熟练的原因。不管了,下面开始本次练习记录。
BOSS选择2
既然上一次选择了史莱姆王作为游戏的第一个BOSS,而且也算是比较容易实现的,这次不妨直接用泰拉瑞亚的第二个BOSS作为小恐龙游戏的第二个boss吧。泰拉瑞亚中一个比较出名的存在--克眼。这一个boss其实有两个阶段,但是我在游戏里只写了一个,感觉第二阶段可以作为别的再写一次。
新敌人:小眼
这次也想之前一样设计了一个新的敌人,因为我发现泰拉瑞亚里面的很多BOSS都会召唤小弟,所以感觉每写一个boss都可以把小弟的内容给写进去,而且把小弟的脚本和BOSS的脚本分开,还可以让小弟作为新的敌人在游戏里生成。
然后就是这个小弟的设计,在原本的游戏里,这个小弟的技能也就一种,朝着玩家冲过去。这个没啥意思,因为我们之前编写的翼龙也会朝玩家冲去。所以我自己把这个小弟设计为,一个阻挠玩家的存在,他不会直接冲向玩家,而是盘绕在玩家周围,在玩家身前或者身后形成一堵墙一样的东西,然后到达位置之后就会自毁,产生爆炸,阻挠玩家躲避BOSS技能的存在。
基本思路是获取玩家的position然后随机获得y轴坐标以及x轴坐标,赋予速度。
关于这个速度,untiy里面的刚体自带了一个velocity的对象,他是一个Vector3类型,可以给对象赋予速度,然后同时在unity里面还存在其他让对象运动的函数,比如Addforce之类的,还有一些特殊的存在比如:MovePosition(),translate()等,后者需要放在update里面每一帧执行,而velocity只需要执行一次,对象就会获得一个恒定的速度。在我安排小弟移动的时候,不小心把rig.velocity = vel*speed;这样的方法放在了update里面使得每一帧都有一个速度,再加上不断判定玩家的位置,导致速度不断叠加,然后使得速度变成无限了。其实主要是speed指数上升了,希望下次不要犯这样的错误。
这是使用的小弟和爆炸图片;
回归正题,按照这样设计之后,获得的代码为:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class littleEyeMove : MonoBehaviour
{
[Header("Set in Inspector")]
public GameObject dino;
public Vector3 dinoPos;
public Rigidbody rig;//刚体对象
public float liveTime;//存活时间
public GameObject explode;//记录一个爆炸的对象
//设计成一个在某个地方爆破的敌人
public Vector3 pos;//记录一个位置
public float timeSummon;//生成时间
public Vector3 vel;
// Start is called before the first frame update
void Start()
{
dino = GameObject.Find("dino");//找到恐龙对象
dinoPos = dino.transform.position;//获取恐龙的基本坐标
pos= dinoPos;//获取第一个位置
liveTime = 3f;//存活时间为三秒
rig = GetComponent<Rigidbody>();//获取刚体对象
timeSummon = Time.time;//获取初始时间
int num = Random.Range(0, 2);
switch (num)//这里是想随机分配位置
{
case 0:
pos.x -= 1f;
break;
case 1:
pos.x += 1f;
break;
}
pos.y += (float)Random.Range(0,2);//获取一个随机y轴位置
vel = pos - gameObject.transform.position;//获取速度
vel.Normalize();
rig.velocity = vel*2f;//给予一个速度
}// Update is called once per frame
void Update()
{
TrackPlayer();//注视玩家
if (Time.time - timeSummon>=liveTime)//靠近了位置
{
rig.velocity = Vector3.zero;//停止移动
GameObject sm = Instantiate(explode);//生成爆炸的对象
sm.transform.position = gameObject.transform.position;
Invoke("Dest", 0.1f);
}
}
//这个代码是在BOSS那里复制过来的,毕竟也是眼珠子
void TrackPlayer()//眼睛紧跟着玩家
{
Vector3 eyePos = gameObject.transform.position;//获取眼睛当前位置
dinoPos = dino.transform.position;//获取玩家坐标
float length = (dinoPos - eyePos).magnitude;//获得长度
float Ang = Mathf.Abs((dinoPos.y - eyePos.y) / length);//获得一个sin值
float A2 = (Mathf.Asin(Ang) * Mathf.Rad2Deg);//反sin函数加上弧度制获得角度
if (eyePos.y <= dinoPos.y)//假如在下方
{
A2 += 90f;//加上九十度
}
//在左边
//计算确切角度;
if (eyePos.y >= dinoPos.y)//在上方需要修改
{
A2 = 90f - A2;
}
if (eyePos.x <= dinoPos.x)//在左边
{
gameObject.transform.rotation = Quaternion.Euler(0, 0, A2);
}
else
{
gameObject.transform.rotation = Quaternion.Euler(0, 0, -A2);
}}
void Dest()
{
Destroy(gameObject);
}
}
上面那个注视玩家的代码在BOSS设计的时候再解释。
Boss--克眼
设计这一个boss,我决定给他添加一些有意思的机制,不然也就老三样。这个BOSS在原游戏里面,是一直注释着玩家的,那我也可以通过untiy自带的transform进行旋转。
unity自带的2D其实也是具有z轴的,指向电脑屏幕,因此可以通过绕z轴旋转,达成效果。
下载得到的图片,transform在(0,0,0)状态下,眼睛往下看,那么根据观察得到:
旋转的参考为:当眼在玩家左边时,需要对角度进行加法,在右边则进行减法。然后眼在上边需要对旋转的角加上90度。根据这一个可以直接利用unity自带的反三角函数获得角度,首先可以得到一个sin值,用对边比斜边得到。但是现在又存在一个问题就是,实际上,通过y轴/magitude的绝对值,假如眼在上方,得到的并非为我们所需要的那个角的sin值,因此我们需要用九十度减去这一个角。还有就是Unity的反三角函数计算出来的是弧度制的值需要乘以57.3才可以得到角度。
自此,这一个思路已经完全可以成立
对于看着玩家的代码为:
void TrackPlayer()//眼睛紧跟着玩家
{
Vector3 eyePos = gameObject.transform.position;//获取眼睛当前位置
dinoPos=dino.transform.position;//获取玩家坐标
float length = (dinoPos - eyePos).magnitude;//获得长度
float Ang = Mathf.Abs((dinoPos.y - eyePos.y) / length);//获得一个sin值
float A2 = (Mathf.Asin(Ang) * Mathf.Rad2Deg);//反sin函数加上弧度制获得角度
if (eyePos.y <= dinoPos.y)//假如在下方
{
A2 += 90f;//加上九十度
}
//在左边
//计算确切角度;
if (eyePos.y >= dinoPos.y)//在上方需要修改
{
A2 = 90f - A2;
}
if (eyePos.x <= dinoPos.x)//在左边
{
gameObject.transform.rotation = Quaternion.Euler(0,0,A2);
}
else
{
gameObject.transform.rotation = Quaternion.Euler(0, 0, -A2);
}}
这样眼珠子就会一直看着玩家了。
下一个设计:
Boss会在不释放技能时盘绕在玩家周围,但是我没有写随机盘绕,我设计为,Boss会在以玩家2x2的范围内的角处停留,玩家移动则跟着移动。 然后释放一次技能之后就会换一个选点继续盘旋。
然后逛完四个点之后重置,同时超出边界自动回到停留点:
大概图示时这样:
差不多就这样然后基本的代码是:
dinoPos = dino.transform.position;//获取主角位置
if (canMove == true)
{
pos = gameObject.transform.position;
standNext = dinoPos;
standNext.x += stanroll[standNum].x; standNext.y += stanroll[standNum].y;
if ((pos - standNext).magnitude <= 0.1f)//假设两者距离小于0.1
{
rig.velocity = Vector3.zero;
gameObject.transform.position = standNext;
}
else
{
Vector3 vel = standNext - gameObject.transform.position;
vel.Normalize();
rig.velocity = vel * 2f;
}}
private Vector2[] stanroll = new Vector2[]
{
new Vector2(3f,3f),new Vector2(-3f,3f),new Vector2(-3f,-1f),new Vector2(3f,-1f)
};
上面这个就是四个角的坐标记录,方便使用。
最后攻击方式只写了两个:叫小弟和冲刺,没什么太大的问题,直接上代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class KEyeMove : MonoBehaviour
{
[Header("Set in Inspector")]
public GameObject smallEye;//保存小眼珠子的对象
public Rigidbody rig;//刚体对象
public GameObject dino;
public Vector3 dinoPos;
public Vector3 pos;
public float timeNow;//记录当前时间
public float liveTime;//存活时间
public float timePast;//技能间隔
public float summonTime;
public Vector3 standNext;//设立一个位置记录下一个位置
public Vector3 standPast;//记录上一个位置
private int roll;//技能使用的色子
public int standNum = 0;
private Vector2[] stanroll = new Vector2[]
{
new Vector2(3f,3f),new Vector2(-3f,3f),new Vector2(-3f,-1f),new Vector2(3f,-1f)
};
public bool canMove;
private bool isSkill;
private bool isTrack;
public Vector3 vel = Vector3.zero;// Start is called before the first frame update
void Start()
{
dino = GameObject.Find("dino");
rig = GetComponent<Rigidbody>();
timeNow = Time.time;
summonTime = Time.time;
liveTime=60f;
timePast = 3f;//技能间隔为三秒
standPast = new Vector3(32.25f, 8.17f, 0);
gameObject.transform.localPosition = standPast;
standNext = Vector3.zero;
pos = gameObject.transform.position;//初始化自身位置
standNum = 0;
canMove = true;
isSkill = false;
isTrack = false;
}// Update is called once per frame
void Update()
{
if (Time.time - summonTime >= liveTime)
{
Destroy(gameObject);
}
TrackPlayer();
dinoPos = dino.transform.position;//获取主角位置
if (canMove == true)
{
pos = gameObject.transform.position;
standNext = dinoPos;
standNext.x += stanroll[standNum].x; standNext.y += stanroll[standNum].y;
if ((pos - standNext).magnitude <= 0.1f)//假设两者距离小于0.1
{
rig.velocity = Vector3.zero;
gameObject.transform.position = standNext;
}
else
{
Vector3 vel = standNext - gameObject.transform.position;
vel.Normalize();
rig.velocity = vel * 2f;
}}
if(Time.time - timeNow >= timePast)//达到了技能释放的时间
{
canMove = false;//停止围绕运动
IsSkill();//更改isskill属性
//重置时间
switch(roll)//根据roll进行移动
{
case 0:
for(int i=0;i<5;i++)//随机生成小弟
{
GameObject le = Instantiate(smallEye);//实例化小眼珠子
Vector3 smPos = gameObject.transform.position;
smPos.y += (float)i * 0.2f;//存在间隔
le.transform.position = smPos;//生成小眼珠子
}
canMove = true;//可以移动
StandNumplus();//切换跟随地点
timeNow = Time.time;
roll = Random.Range(0, 2);//随机抽取
break;
case 1:
if (isTrack == false)
{
dinoPos = dino.transform.position;//获取玩家位置
standNext = dino.transform.position;
vel = dinoPos - gameObject.transform.position;//获取速度位置
int num = Random.Range(0,2);
switch (num)
{
case 0:
vel.x += 1f;
standNext.x += 1f;
break;
case 1:
vel.x -= 1f;
standNext.y -= 1f;
break;
//随机寻找一个坐标,前一点或者后一点
}
vel.x *= 2f; vel.y *= 2f;//加速
rig.velocity = vel;//进行加速
isTrack = true;
}
if(isTrack == true)//假如正在跟踪
{
Vector3 PPos = gameObject.transform.position;//获取位置
if ((gameObject.transform.position - standNext).magnitude <= 0.5f||PPos.x>=32.5f||PPos.x<=18.5f||PPos.y<=1.7f||PPos.y>=10.2f)//已经到达目的地附近
{//上面添加了一个判定,为了防止一飞冲天,导致没办法重设的问题
rig.velocity = Vector3.zero;//速度归零
isTrack = false;
StandNumplus();//换取选点
timeNow = Time.time;//重置时间
canMove = true;//允许移动
roll = Random.Range(0, 2);//随机选点
}
}
break;
}
}
}
void TrackPlayer()//眼睛紧跟着玩家
{
Vector3 eyePos = gameObject.transform.position;//获取眼睛当前位置
dinoPos=dino.transform.position;//获取玩家坐标
float length = (dinoPos - eyePos).magnitude;//获得长度
float Ang = Mathf.Abs((dinoPos.y - eyePos.y) / length);//获得一个sin值
float A2 = (Mathf.Asin(Ang) * Mathf.Rad2Deg);//反sin函数加上弧度制获得角度
if (eyePos.y <= dinoPos.y)//假如在下方
{
A2 += 90f;//加上九十度
}
//在左边
//计算确切角度;
if (eyePos.y >= dinoPos.y)//在上方需要修改
{
A2 = 90f - A2;
}
if (eyePos.x <= dinoPos.x)//在左边
{
gameObject.transform.rotation = Quaternion.Euler(0,0,A2);
}
else
{
gameObject.transform.rotation = Quaternion.Euler(0, 0, -A2);
}}
void IsSkill()
{
isSkill = !isSkill;//更改释放技能属性
}
void StandNumplus()
{
if (standNum >= 3)
{
standNum = 0;
}
else
{
standNum ++;
}
}
}
以上就是这一个BOSS的设计了,其实也是比较简单的设计,不过感觉还可以。
以下为添加之后的游戏实例图:
结语
这此这个练习就到此为此啦,我还会继续添加更多的内容,丰富自己的 代码经验,提高能力,谢谢大家!!