目录

使用Unity做一个3D吃豆人小游戏

使用Unity做一个3D吃豆人小游戏

目标

用Unity完成一个3D吃豆人小游戏,实现玩家吃豆子,鬼魂追逐玩家的功能。

制作步骤

制作模型

在右上角的Layout选择2×3,方便操作。

在层级界面右键选择3D对象,添加模型对象。

https://i-blog.csdnimg.cn/direct/4131b90f2c3045828c185b1822ee9fe1.png

选择平面,构建迷宫底,可以在右边的Transform调整位置和大小。

https://i-blog.csdnimg.cn/direct/c777186ef5484aeaaaceaf65af6b355d.png

同理可以新建立方体构建迷宫的围墙。

https://i-blog.csdnimg.cn/direct/158775c5960f4398b0cd2168f7fcb02d.png

若要更换颜色等材质可以在Assets栏右键,选择创建→材质(左图)。

选择基础贴图,选取颜色(右图),再将材质拖到想改变颜色的模型对象上,即可完成颜色更换。

https://i-blog.csdnimg.cn/direct/7f192e26155045ebbb83701ef0a33a58.png https://i-blog.csdnimg.cn/direct/9a5d5ad44dba4659ae4754945198a378.png

https://i-blog.csdnimg.cn/direct/867d466b4f2549bf8be8c185cf3f494a.png

左下角的游戏视角可以通过选择Main Camera并进行修改。

https://i-blog.csdnimg.cn/direct/330a1b4cba5e43a7b374ed1730e7a0aa.png

设计完地图后大概是这样(迷宫内部可以自行修改)此处我用了黄色圆球表示吃豆人,浅黄色小球表示豆子,三个椭球表示鬼。

https://i-blog.csdnimg.cn/direct/ef0d6bdbb55a47a6af5dbba06ab782f8.png

至此3D部分的模型就差不多做好了。

再新建一个画布,并在画布上右键选中UI→面板,命名为winPanel,再新建一个文本作为游戏胜利 的显示。

https://i-blog.csdnimg.cn/direct/ba64cbec7ec04dbda69d58ea908ccdba.png

同理,也设计出一个GameOver界面。

之后再在这两个界面之上设置一个Botton,Botton之内添加一个文本,自定义按键中的文字。

整体的架构和效果大概分别如下图:

https://i-blog.csdnimg.cn/direct/e692c540abaa4d5eb013da9a38b9278e.png

https://i-blog.csdnimg.cn/direct/479353036280478893bcc897e46b995c.png

现在模型部分基本都已完成,可以开始进行之后的设计。

逻辑设计

选中所有墙壁,在检查器的标签处添加Wall标签,并将Collider开启。

https://i-blog.csdnimg.cn/direct/c6c145534d544d6ab3d6b25fe936ede1.png

https://i-blog.csdnimg.cn/direct/95009ee5dd2f43ca96565544547f125a.png

吃豆人的标签设为Player,同样也将Collider开启,并添加Rigidbody组件,以便完成移动功能。

在Rigidbody组件中,取消“是运动学的”选项,并冻结位置和旋转,以防飞出地图外或乱动。

鬼的标签设为Ghost,Collider和Rigidbody的配置和吃豆人一样。

https://i-blog.csdnimg.cn/direct/68c737955983427bbc41251e83777b39.png

选中所有豆子,豆子的标签设为Bean,不需要Rigidbody组件,但同样需要开启Collider,且需选中“是触发器”选项。

https://i-blog.csdnimg.cn/direct/b5727de1a495482ea2ec392599be3180.png

要实现鬼魂自动追逐玩家的效果,需要使用组件Nav Mesh Agent,导入组件后可在组件内部设置各个鬼的移动速度等。(三个鬼都需要)

PS:为防止鬼挤在一起,可以给它们设置不同的速度。

https://i-blog.csdnimg.cn/direct/d91f7148e9e84e4e826a34fdf712cee8.png

之后进行鬼魂移动区域的规划,需要在工具栏的窗口处选中AI→导航(过时)进行导入

(可能需要在包管理器处下载AI Navigation才能显示)

https://i-blog.csdnimg.cn/direct/1dce293d2101462c8cc10aaa39571873.png

导入后选择 Plane ,选中Navigation Static。

https://i-blog.csdnimg.cn/direct/2a44efc0ce714e80af64f6f8fcb7e0f5.png

再选择烘培窗口,点击Bake。

https://i-blog.csdnimg.cn/direct/4389ce9da5194883883f0bef9fcbc411.png

之后就可以完成自动追踪吃豆人的功能。

但要实现游戏的运行还有最重要的代码部分。

代码设计

PacmanController.cs:

using UnityEngine;

public class PacmanController : MonoBehaviour
{
    public float moveSpeed = 30f;  // 设置吃豆人的移动速度
    private Rigidbody rb;

    void Start()
    {
        rb = GetComponent<Rigidbody>();
    }

    void Update()
    {
        MovePacman();
    }

    // 控制吃豆人的移动
    void MovePacman()
    {
        float moveX = Input.GetAxis("Horizontal");  // 读取左右键输入
        float moveZ = Input.GetAxis("Vertical");    // 读取上下键输入

        // 保持吃豆人在平面上移动
        Vector3 movement = new Vector3(moveX, 0, moveZ) * moveSpeed * Time.deltaTime;
        rb.MovePosition(transform.position + movement);
    }

    // 吃豆子逻辑
    void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.CompareTag("Bean"))
        {
            Destroy(other.gameObject);  // 移除豆子

            // 告知GameManager豆子已被吃掉
            FindObjectOfType<GameManager>().AddCollectedBean();
        }
    }
}

在这个代码内我们设置了吃豆人的逻辑,如设置移动速度为30,设定其移动由键盘的上下左右控制,设置吃豆人碰到豆子就将豆子移除并告知GameManager以达成吃豆子的效果。

GhostController.cs:

using UnityEngine;
using UnityEngine.AI;

public class GhostController : MonoBehaviour
{
    public Transform player;  // 玩家(吃豆人)的Transform
    private NavMeshAgent navMeshAgent;

    void Start()
    {
        navMeshAgent = GetComponent<NavMeshAgent>();  // 获取鬼的NavMeshAgent组件
    }

    void Update()
    {
        // 让鬼始终追逐玩家
        navMeshAgent.SetDestination(player.position);
    }

    // 当鬼碰到玩家时触发游戏结束
    void OnCollisionEnter(Collision collision)
    {
        // 检查碰到的物体是否是玩家
        if (collision.gameObject.CompareTag("Player"))
        {
            Debug.Log("鬼碰到玩家");
            FindObjectOfType<GameManager>().GameOver(false);  // 显示游戏结束界面
        }
    }
}

此代码用于实现鬼的逻辑,使其自动追逐玩家,并在玩家被鬼碰到时触发GameManager的GameOver函数以达到游戏结束的效果。

GameManager.cs:

using UnityEngine;
using UnityEngine.SceneManagement;  // 用于重新加载场景

public class GameManager : MonoBehaviour
{
    private bool isGameOver = false;
    private int beansCollected = 0;  // 吃掉的豆子数量
    public int totalBeans = 48;  // 需要吃掉的豆子总数
    private UIManager uiManager; // 引用UIManager

    void Start()
    {
        uiManager = FindObjectOfType<UIManager>(); // 获取UIManager实例
    }

    // 每次吃到一个豆子时调用这个方法
    public void AddCollectedBean()
    {
        beansCollected++;  // 增加豆子计数器
        Debug.Log("吃掉的豆子数: " + beansCollected);

        // 如果吃满48个豆子,触发胜利
        if (beansCollected >= totalBeans)
        {
            GameOver(true);  // 显示胜利界面
        }
    }

    // 游戏结束处理
    public void GameOver(bool isWin)
    {
        if (isGameOver) return;  // 防止重复触发
        isGameOver = true;

        if (isWin)
        {
            Debug.Log("胜利!");
            uiManager.ShowWin(); // 调用UIManager显示胜利界面
        }
        else
        {
            Debug.Log("游戏结束");
            uiManager.ShowGameOver(); // 调用UIManager显示游戏结束界面
        }

        Time.timeScale = 0f;  // 暂停游戏
    }

    // 重新开始游戏
    public void RestartGame()
    {
        Time.timeScale = 1f;
        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);  // 重新加载当前场景
    }
}

此代码对游戏的数据进行了管理,也设置了当吃豆人吃完了界面上所有豆子后就调用GameOver函数触发胜利界面的胜利条件。

UIManager.cs:

using UnityEngine;
using UnityEngine.UI;
using TMPro;  // 引入TextMeshPro命名空间

public class UIManager : MonoBehaviour
{
    public Button restartButton;   // 重新开始按钮
    public GameObject gameOverPanel; // 游戏结束面板
    public GameObject winPanel;      // 胜利面板
    private GameManager gameManager;
    private TMP_Text restartButtonText; // 使用TextMeshPro

    void Start()
    {
        gameManager = FindObjectOfType<GameManager>();

        // 绑定重新开始按钮的点击事件
        restartButton.onClick.AddListener(RestartGame);

        // 初始隐藏结束和胜利面板,以及重新开始按钮
        gameOverPanel.SetActive(false);
        winPanel.SetActive(false);
        restartButton.gameObject.SetActive(false); // 隐藏按钮
    }

    // 显示游戏结束界面
    public void ShowGameOver()
    {
        gameOverPanel.SetActive(true);  // 显示游戏结束面板
        winPanel.SetActive(false);      // 确保胜利面板隐藏
        restartButton.gameObject.SetActive(true);  // 显示重新开始按钮

        // 确保重新开始按钮位于UI层级的最上方
        restartButton.transform.SetAsLastSibling();
    }

    // 显示胜利界面
    public void ShowWin()
    {
        winPanel.SetActive(true);      // 显示胜利面板
        gameOverPanel.SetActive(false); // 确保游戏结束面板隐藏
        restartButton.gameObject.SetActive(true);  // 显示重新开始按钮

        // 确保重新开始按钮位于UI层级的最上方
        restartButton.transform.SetAsLastSibling();
    }

    // 重新开始游戏
    public void RestartGame()
    {
        gameManager.RestartGame();  // 调用 GameManager 的重新开始方法

        // 隐藏所有面板和按钮
        gameOverPanel.SetActive(false); 
        winPanel.SetActive(false);      
        restartButton.gameObject.SetActive(false); // 隐藏重新开始按钮
    }
}

此代码用于管理UI界面,在游戏运行时将胜利、结束界面及重新开始按钮隐藏,在显示游戏胜利或结束时显示对应的UI画面。

实现完以上代码后,需要将代码拖入对象中完成代码的使用。

选中吃豆人对象,打开其检查器,将PacmanController代码拖入其中,效果如下,可以在Move Speed处修改吃豆人运行速度。

https://i-blog.csdnimg.cn/direct/eb7550e32a8c4f349f8977b723e8cb0e.png

鬼同理,将GhostController代码拖入三个鬼当中,但需要将“玩家”指向的对象修改为吃豆人(三个都要)

https://i-blog.csdnimg.cn/direct/4389fb1072b1435f8fb9331cf9590648.png

https://i-blog.csdnimg.cn/direct/23691d3bb9f3464b8c673257a1677580.png

UIManager代码则拖入UI对象当中,同样需要修改指向对象。

https://i-blog.csdnimg.cn/direct/182df4660b1243bda7b6d8dde85b3739.png

至于GameManager代码,则需要新建一个空对象,再将代码拖入。

至此已经完成了游戏的所有内容,可以进行游玩。