贪吃豆主要是用SpriteRenderer进行开发,是2D的,其中有豆子、玩家黄豆、敌人四个豆子(不同颜色),一张贪吃豆的地图。
贪吃豆地图的碰撞体设置会有些许麻烦,那就是要在它身上挂载很多个碰撞体来实现整张地图的碰撞体。
豆子需要一个碰撞体(勾选Trigger)和一个脚本进行管理豆子,一般都是处理豆子的消亡和加分逻辑等,脚本如下。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Pacdot : MonoBehaviour {
public bool isSuperPacdot = false;
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.gameObject.name=="Pacman")
{
if (isSuperPacdot)
{
//吃豆后把吃掉的豆子所记录在pacdotGos列表中的豆子删除
GameManager.Instance.OnEatPacdot(gameObject);
//吃掉超级豆子后执行的各种效果
GameManager.Instance.OnEatSuperPacdot();
//删除该豆子
Destroy(gameObject);
}
else
{
GameManager.Instance.OnEatPacdot(gameObject);
Destroy(gameObject);
}
}
}
}
玩家黄豆需要一个碰撞体、一个2D刚体(将重力设置为0)、一个Animator用于控制玩家黄豆动画播放的,例如:上、下、左、右都是有一个张嘴合嘴的序列帧动画,也就是四个动画,在动画管理器里面设置2个变量来进行控制这四个动画在何时播放,具体代码在下面给出,下面的代码是挂在玩家黄豆身上的,以用于控制玩家移动、动画播放等。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Move : MonoBehaviour
{
public float speed = 0.35f;//速度
private Vector2 dest = Vector2.zero;//目的地位置
private void Start()
{
dest = transform.position;//初始位置设置
}
private void FixedUpdate()
{
//插值获取到达dest位置的下一个位置temp
Vector2 temp = Vector2.MoveTowards(transform.position, dest, speed);
//移动到temp位置
GetComponent<Rigidbody2D>().MovePosition(temp);
//必须到达上一次dest位置,才可以重新开始新的dest目的地位置
if ((Vector2)transform.position == dest)
{
if ((Input.GetKey(KeyCode.UpArrow) || Input.GetKey(KeyCode.W))&& Valid(Vector2.up))
{
dest = (Vector2)transform.position + Vector2.up;
}
if ((Input.GetKey(KeyCode.DownArrow) || Input.GetKey(KeyCode.S)) && Valid(Vector2.down))
{
dest = (Vector2)transform.position + Vector2.down;
}
if ((Input.GetKey(KeyCode.LeftArrow) || Input.GetKey(KeyCode.A)) && Valid(Vector2.left))
{
dest = (Vector2)transform.position + Vector2.left;
}
if ((Input.GetKey(KeyCode.RightArrow) || Input.GetKey(KeyCode.D))&& Valid(Vector2.right))
{
dest = (Vector2)transform.position + Vector2.right;
}
//获取移动方向
Vector2 dir = dest - (Vector2)transform.position;
//把获取到的移动方向设置给动画状态机
GetComponent<Animator>().SetFloat("DirX", dir.x);
GetComponent<Animator>().SetFloat("DirY", dir.y);
}
}
//检测将要去的位置是否可到达
private bool Valid(Vector2 dir)
{
Vector2 pos = transform.position;
//hit 是接收到碰到的物体
RaycastHit2D hit = Physics2D.Linecast(pos + dir, pos);//从到达的位置发射一条射线到自身位置
return (hit.collider == GetComponent<Collider2D>());//若能碰到自身,那么说明可以移动到那个位置,若不可以则说明不能到达
}
}
敌人四个豆子需要一个碰撞体(需要勾选Trigger)、一个2D刚体(重力为0)、一个Animator和上面一样,动画也是有4个,也是有一样意义的2个参数,所以我们可以在创建Animator的时候,点击Animator Override Controller,然后把玩家黄豆的Animator拖过去,就能生成基本一样的Animator(即条件、动画个数一样),然后将里面的四个动画改为敌人的四个动画即可,最后需要挂载一个脚本控制敌人移动、动画播放等,代码如下。其中,四个敌人都是在2、3、4、5层级(SpriteRenderer组件的Order in layer)的,玩家黄豆在6层级,豆子在1层,地图本身在0层。此外,敌人的移动是固定路径的,也就是路径点是分配好了的,敌人只需一个点一个点地走就OK了,那个wayPointsGos是保存了4个物体,这些物体的子物体们都是路径点,也就是一些空物体,主要是我们要获取position就好,这些物体都是在开发前制作好的路径点物体预制体,直接用public方式传递给脚本的。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class GhostMove : MonoBehaviour {
//存储所有路径点的Transform
public GameObject[] wayPointsGos;
public float speed = 0.2f;
private List<Vector3> wayPoints = new List<Vector3>();
private int index = 0;//当前在前往哪个路径点中
private Vector3 startPos;
public GameObject canvas;
private void Start()
{
startPos = transform.position + new Vector3(0, 3, 0);
LoadPath(wayPointsGos[GameManager.Instance.usingIndex[GetComponent<SpriteRenderer>().sortingOrder-2]]);
}
private void FixedUpdate()
{
//鬼移动方法:
//若鬼没有到达目的地则继续移动
if (transform.position != wayPoints[index])
{
//插值获取到达wayPoints[index].position位置的下一个位置temp
Vector2 temp = Vector2.MoveTowards(transform.position, wayPoints[index], speed);
//移动到temp位置
GetComponent<Rigidbody2D>().MovePosition(temp);
}
else//若到达目的地,则选择新的目的地
{
index++;
if(index>=wayPoints.Count)
{
index = 0;
LoadPath(wayPointsGos[Random.Range(0, 4)]);
}
}
//获取移动方向
Vector2 dir = wayPoints[index] - transform.position;
//把获取到的移动方向设置给动画状态机
GetComponent<Animator>().SetFloat("DirX", dir.x);
GetComponent<Animator>().SetFloat("DirY", dir.y);
}
/// <summary>
/// 鬼碰到玩家,则销毁玩家
/// </summary>
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.name == "Pacman")
{
if (GameManager.Instance.isSuperPacman)
{
//碰到超级吃豆人,该怪物回家
transform.position = startPos - new Vector3(0, 3, 0);
index = 0;//从路径头部开始
//吃了鬼 给奖励分
GameManager.Instance.score += 500;
}
else//如果碰到的是普通吃豆人,则销毁玩家(吃豆人)
{
collision.gameObject.GetComponent<Collider2D>().enabled = false;
collision.gameObject.SetActive(false);
GameManager.Instance.gamePanel.SetActive(false);
GameObject go = Instantiate(GameManager.Instance.gameOverPrefab,canvas.transform);
go.transform.SetParent(canvas.transform);
Invoke("ReStart", 3f);
}
}
}
private void LoadPath(GameObject go)
{
//加载路径点前,先清空存放的列表
wayPoints.Clear();
//遍历wayPointsGo物体身上所有子物体的Transform
foreach (Transform t in go.transform)
{
wayPoints.Add(t.position);//把子物体的Position存放入List中
}
wayPoints.Insert(0, startPos);
wayPoints.Add(startPos);
}
private void ReStart()
{
SceneManager.LoadScene(0);
}
}
UI部分的脚本就不说了,基本如上所述一个2D贪吃豆游戏就基本完成了,其中,豆子是有一个叫超级豆子的玩意的,玩家吃了后能把敌人吃回家,不会受到敌人的攻击而死亡,加速之类的。超级豆子是在一开始就从一堆豆子中随机选一个把它变为超级豆子的,其实代码也就是一个bool变量的事情,为false是普通,为true是超级豆子,超级豆子的图片变大点就能让玩家知道这个豆子是超级豆子,你也可以特别地为超级豆子选个牛逼点的图片。