当前位置: 首页 > article >正文

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

目标

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

制作步骤

制作模型

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

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

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

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

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

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

 

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

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

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

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

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

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

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

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

逻辑设计

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

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

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

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

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

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

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

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

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

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

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

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

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

代码设计

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处修改吃豆人运行速度。

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

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

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

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


http://www.kler.cn/a/445957.html

相关文章:

  • 免费GIS工具箱:轻松将glb文件转换成3DTiles文件
  • 虚拟机断网没有网络,需清理内存,删除后再重启
  • Moretl开箱即用日志采集
  • redis数据类型:list
  • 基础数据结构---栈
  • excel 使用vlook up找出两列中不同的内容
  • 【人工智能】物联网技术及其应用
  • 电商店铺数据集成到金蝶云星辰V2的实践经验分享
  • k8s-1.28.1证书更新到100年-cenots7.9
  • 卷积神经网络-填充+步长
  • 差分数组的使用
  • 工业主板产品线的多样性与应用
  • 【数据分析之pandas】
  • go语言学习之错误记录-1、GOPROXY
  • 原生开发vs混合开发
  • 【上传文件过大进行的切片式上传】
  • Oracle 表连接原理与优化
  • Qt 开发之蓝牙连接
  • hackme靶机保姆及攻略
  • elasticsearch Flattened 使用
  • JS面向对象及继承
  • 【第六节】Git Flow:分支管理模型与工作流程
  • nano编辑器怎么退出并保存
  • DeepFaceLab技术浅析(六):后处理过程
  • Golang中什么是协程泄露(Goroutine Leak)
  • autok3s管理k3s单节点集群