简介
这是我的第二个项目,与之前第一项目不同,这个项目涉及的专业知识要求更高一些,这个项目的重点在于A*算法的使用、AI状态机的使用,因此学习难度也比较好。所以我将这个项目分成几个部分。
第一步 场景搭建
与第一个项目相类似,这里就不多重复。这里查看我的第一个项目
第二步 动画的创建
一个动画控制器,四个方向的动画片段。
因为动画有上下左右四个动画,所以要相互转换,因此由Any State 向四个不同动画状态转换
在设置转换条件的时候,将Parameters 添加Float类型,因为当玩家操控角色进行上下左右移动时,总的来说就是控制物体进行坐标位置的增减。例如 右移一个单位就是DirX+1 ,而左移动一个单位就是 DirX-1。
注意:不能将float的判断条件设置为零,因为对于计算机来说,一个非常小的数也会被默认为零,会造成不准确。所以尽可能设置为0.1 、-0.1 …这样的数。
第三步 主角的移动控制
具体代码
public class PacmanMove : MonoBehaviour
{
public float speed = 4f;
public Vector2 dest;//PacMan行走的目标位置
private void Start()
{
dest = this.transform.position;
}
public void FixedUpdate()//物理上每一帧,有物理组件时就要用FixedUpdate,Update控制的移动仅仅是图片上的移动
{
//1、控制角色朝着目标方向移动
Vector2 p = Vector2.MoveTowards(this.transform.position, dest, speed * Time.fixedDeltaTime);//从当前位置向目标位置移动,每秒移动speed个单位
this.gameObject.GetComponent<Rigidbody2D>().MovePosition(p);//移动位置
//2、如果角色移动到目标点了,则检测玩家按键,通过上下左右控制角色
if ((Vector2)this.transform.position == dest)//二维不能与三维直接判断,但是可以赋值
{
Vector2 dir = Vector2.zero;
if (Input.GetKey(KeyCode.DownArrow) && CanGo(Vector2.down))
{
dir = Vector2.down;
}
if (Input.GetKey(KeyCode.UpArrow) && CanGo(Vector2.up))
{
dir = Vector2.up;
}
if (Input.GetKey(KeyCode.RightArrow) && CanGo(Vector2.right))
{
dir = Vector2.right;
}
if (Input.GetKey(KeyCode.LeftArrow) && CanGo(Vector2.left))
{
dir = Vector2.left;
}
GetComponent<Animator>().SetFloat("DirX",dir.x);//按键来控制动画
GetComponent<Animator>().SetFloat("DirY", dir.y);
dest += dir;
}
}
private bool CanGo(Vector2 dir)
{
RaycastHit2D hit = Physics2D.Linecast(this.transform.position, (Vector2)this.transform.position + dir, 1 << LayerMask.NameToLayer("Map"));//发出射线,由pacman发出射线,射线的层由1位移到Map层,当碰到墙后,反映到hit上,有碰撞物就返回true,没有就返回flase
//return hit != true;//=>hit.operator bool
return hit!=true;//相当于ture!=ture 返回flase
}
}
这一部分代码虽然不长,但是知识点十分密集。
-
主角移动的步骤
1、控制角色朝着目标方向移动
2、如果角色移动到了目标端,则检测玩家按键,通过上下左右控制 -
逻辑分析
1、先定义一个位置(dest),
2、在开始时将当前的位置赋值给dest
3、在按键后给当前位置增加一个单位
4、调用MoveTowards方法使用一个dest数值,进行让角色向目标方向移动
5、角色实现移动 -
角色移动的实现
Vector2.MoveTowards(位置1,位置2,每秒移动x个单位)
位置1:this.transform.position 现有位置
位置2: dest 即将要抵达的位置
每秒移动x个单位:speed * Time.fixedDeltaTime 每秒按speed个单位移动
效果:就是让游戏物体不会直接移动到目标位置,而是有一个过程
this.gameObject.GetComponent<Rigidbody2D>().MovePosition(p)
调用MovePosition()方法,使带有刚体的游戏物体移动到p的位置
注意
这里的游戏物体的移动是建立在具有刚体组件的条件上的,这里就要区分FixedUpdate()与Update()方法的区别了。
FixedUpdate()方法在unity中是比Upadte更早被执行。
FixedUpdate()方法是游戏物体物理的上的移动
Update()方法则是游戏图片的移动
因此如果游戏物体上有物理组件,那么一定要使用FixedUpdate()方法
- 判断角色是否可以移动
原理:通过射线检测前方是否有碰撞体
Vector2 dir 输入按键参数,以确认是否为可以走。
RaycastHit2D 射线类型
Physic2D.Linecast 发出射线
return 一个bool值。
方法分析
Linecast(位置1、位置2、掩码)
位置1 this.transform.postion 当前位置
位置2 this.transform.postion+dir 射线到到达的位置
掩码 1<<LayerMark.NameToLayer(“Map”) 从图层1到为"Map"的图层
返回值:如果有碰撞就返回true,如果没有碰撞就返回flase
注意 RaycastHit2D类型的值是一个重载bool值。在进行按键控制时,会调用这个方法,一定要注意返回者,前面有碰撞时要返回的是true,所以需要返回一个flase来控制不能让角色继续移动。这是就需要使用 true!=true 来返回一个flase(这里我就绕了很旧)。
还有一点,一定要注意角色的碰撞器与障碍的距离,如果角色的碰撞器过大,射线判断就会让角色一直处于碰撞的状态,让角色一直无法移动,或者发生可以左右移动,但是上下的距离不够,造成无法上下移动。
第四步 吃豆子功能
首先为豆子添加碰撞器,并且改为触发器模式
再添加一个脚本Pacdot
public class Pacdot : MonoBehaviour
{
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.name == "pacman")//当碰撞体的name为pacman时摧毁,当前游戏物体
{
Destroy(this.gameObject);
}
}
}