[Unity3D] 3D贪吃蛇

3 篇文章 0 订阅

目录

前言

一、整体设计思路

二、蛇体部分

2.1  蛇头(模仿传统贪吃蛇)

2.2  蛇身

2.3  贪吃蛇(改进版)

三、食物部分

3.1  旋转

3.2  触发器

3.3  生成食物

四、墙体部分

4.1  提供环境

五、胜利和失败的判定

5.1  屏幕显示分数

5.2  游戏胜利

5.3  游戏失败

六、界面设计

6.1  开始界面

6.2  结果界面(胜利/失败)

6.3  分数显示

6.4  退出界面


前言

最近在学习unity,计划做一个3D的贪吃蛇的小游戏,利用这个小游戏锻炼自己使用软件的熟练度。

一、整体设计思路

简单起见,并不进行建模美化操作,以直径为1的球体代替蛇头,直径为0.5的球体代替蛇身,用旋转的立方体代替食物,需要实现的功能有:

1. 蛇体部分

  • 蛇头:移动
  • 蛇身:增长、跟随蛇身运动
  • 视角:跟随

2. 食物部分

  • 旋转
  • 触发器(接触后销毁,并生成一个新的食物)
  • 食物出现在空间内的位置随机

3. 墙体部分

  • 提供食物生成的封闭空间

4. 胜利和失败的判定

  • 屏幕显示分数
  • 分数到达10即为胜利,可以选择继续游戏或结束本局游戏
  • 碰触墙壁或自身身体即为游戏失败,可以选择重新开始或退出游戏

5. 界面设计

  • 开始界面
  • 结果界面(胜利/失败)
  • 分数显示
  • 退出界面

二、蛇体部分

2.1  蛇头(模仿传统贪吃蛇)

蛇头的移动的实现思路是:获取当前相机的前向方向向量,通过按键检测控制移动方向,用一个Vector3 movement向量存储移动的增量,不断增加即可。

相机可以通过鼠标右键进行控制,具体控制代码见另一篇文章:

[Unity3D] 视角旋转学习笔记icon-default.png?t=N3I4https://blog.csdn.net/The___sky_/article/details/130249220?spm=1001.2014.3001.5502

    //蛇头移动+相机跟随
    private void Move()
    {
        // 跟随相机方向一起转动
        cameraRotation = Camera.main.transform.rotation;

        BodyNode.rotation = cameraRotation;
        SnakeRotation = cameraRotation;
        Snake.transform.rotation = SnakeRotation;

        // 获取当前相机的前向方向向量
        Vector3 cameraForward = Camera.main.transform.forward;
        cameraForward = cameraForward.normalized;
        Vector3 cameraRight = Camera.main.transform.right;
        cameraRight = cameraRight.normalized;

        // 计算移动方向
        Vector3 moveDirection = cameraForward * vertical + cameraRight * horizontal;
        Vector3 movement = moveDirection * moveSpeed * deltaTime;    
                
        if (Input.GetKeyDown(KeyCode.W))
        {
            movement = Vector3.forward;
        }
        else if (Input.GetKeyDown(KeyCode.A))
        {
            movement = Vector3.left;
        }
        else if (Input.GetKeyDown(KeyCode.S))
        {
            movement = Vector3.back;
        }
        else if (Input.GetKeyDown(KeyCode.D))
        {
            movement = Vector3.right;
        }
        else if (Input.GetKeyDown(KeyCode.U))
        {
            movement = Vector3.up;
        }
        else if (Input.GetKeyDown(KeyCode.P))
        {
            movement = Vector3.down;
        }
        
        Snake.transform.position += movement;

        // 更新蛇身位置和旋转信息
        for (int i = 0; i < bodyList.Count; i++)
        {
            bodyPositions[i] = bodyList[i].transform.position;
            bodyRotations[i] = bodyList[i].transform.rotation;
        }
    }

2.2  蛇身

蛇身的增长,跟随蛇身移动

    private void Start()
    {
        // 尝试使用动画后的注释
        //bodyList = new List<Rigidbody>();
        //bodyList.Add(BodyNode);
        //bodyPositions = new Vector3[bodyList.Count];
        //bodyRotations = new Quaternion[bodyList.Count];
        CreateFood();
    }


    void LateUpdate()
    {
        bodyList[0].position += movement;

        // 更新蛇身位置和旋转信息
        for (int i = 0; i < bodyList.Count; i++)
        {
            bodyPositions[i] = bodyList[i].transform.position;
            bodyRotations[i] = bodyList[i].transform.rotation;
        }
    }

    private void CreateBody()
    {
        // 尝试使用动画后的注释
        Rigidbody newBody = Instantiate(BodyPrefabs, bodyList[bodyList.Count - 1].transform.position, Quaternion.identity);
        bodyList.Add(newBody);
        bodyPositions = new Vector3[bodyList.Count];
        bodyRotations = new Quaternion[bodyList.Count];      
    }

2.3  贪吃蛇(改进版)

在改进版中,对食物和蛇体导入了外部的3D模型和动画,对场景进行了一定程度的美化,在原有的贪吃蛇的规则上进行了如下改变:

  • 吃到食物后不会增长身体,而是体型增大。
  • 由于导入的外部模型和动画,故而无法实现自己碰到自己身体的条件,所以失败条件改为触碰到墙壁即判定为游戏失败。

在改进版的贪吃蛇中,对蛇体的控制是另外的代码,如果大家感兴趣可以私信我,这里就不做展示了。

三、食物部分

3.1  旋转

新建一个脚本,作用对象是食物,将Update中的代码进行修改。

    void Update()
    {
        //Vector3.up = (0,1,0)
        //Space.World:相对于世界坐标系
        //即绕y轴旋转
        transform.Rotate(Vector3.up, Space.World);
    }

3.2  触发器

按照2D贪吃蛇的逻辑,食物在被蛇头接触后应对其进行销毁,并生成一个新的食物。

注意,如果想要某个游戏物体能够触发触发器,该游戏物体需要带有刚体(Rigidbody)组件。 

首先将其作为触发器,而不是碰撞器,应勾选Is Trigger.

对其新建一个标签,命名为Food,方便后续检测碰撞对象

 在OnTriggerEnter中进行碰撞对象的销毁和新食物的创建(CreateFood)

    private void OnTriggerEnter(Collider other)
    {
        //检测触发器的对象是否是食物
        if (other.tag == ("Food"))
        {
            //是食物则销毁当前碰撞的游戏物体
            Destroy(other.gameObject);
            CreateFood();
        }

    }

3.3  生成食物

食物出现在空间内的随机位置分为三步:

  1. 将预先设置好的食物预制体实例化
  2. 生成x,y,z的随机数
  3. 将生成的随机数作为新生成的食物的位置参数
    void CreateFood()
    {
        //将预制体实例化
        GameObject Foods = Instantiate(FoodPrefabs);

        //随机生成位置
        int x = Random.Range(-9, 9);
        int y = Random.Range(1, 19);
        int z = Random.Range(-9, 9);

        Foods.transform.position = new Vector3(x, y, z);
    }

四、墙体部分

4.1  提供环境

提供食物生成的封闭空间

设置一个预制体,对其进行整体操作。

当前游戏设计中游戏空间为矩形大小,规格为20*20*20,坐标为:x:[-10,10],y:[0,20],z:[-10,10],因此在生成食物时也需要注意不要让食物产生在无法到达的边界。

五、胜利和失败的判定

5.1  屏幕显示分数

新建Text控件,用来显示当前分数。

    // 分数
    private int Score;
    public Text ScoreText;

    // 分数变化
    void ScoreChange()
    {
        Score++;
        ScoreText.text = "Score:" + Score;
    }

5.2  游戏胜利

分数到达10即为胜利。

新建Text控件,用来显示当前分数,一个用来显示结果,显示结果的Text先不给予显示。

    public GameObject Result;
    public Text ResultText;

    // 胜利界面
    void WinInterface()
    {
        Result.SetActive(true);
        if (Score == 10)
        {
            ResultText.text = "You win!!!";
        }
    }

5.3  游戏失败

碰触墙壁或自身身体即为游戏失败,键盘不再可以继续操控小球运动。

    private void Update()
    {
        switch(InterfaceFlag)
        {
            // 胜利
            case 1:
                WinnerInterface();
                break;
            
            // 失败
            case 2:
                GameOverInterface();
                Direction = 0;
                break;
           
             // 默认
            default:
                break;
        }

        if(InterfaceFlag == 0)
        {
            Move();
        }
    }

    // 失败界面
    void GameOverInterface()
    {
        Result.SetActive(true);
        ResultText.text = "Game Over!";
    }

可以选择重新开始或退出游戏

六、界面设计

6.1  开始界面

开始界面和界面的跳转建议读下面这篇文章,写的非常详细,一步一步来就好了,我做的非常简略,就不在这里献丑啦!

unity——通过点击按钮进行场景切换

6.2  结果界面(胜利/失败)

结果界面通过一个公用的Text控件实现,根据不同的游戏结果呈现出不同的文字。

    // 胜利界面
    void WinnerInterface()
    {
        // 激活控件
        Result.SetActive(true);
        ResultText.text = "You win!!!";

        // 停止所有
        Time.timeScale = 0;
        
    }

    // 失败界面
    void GameOverInterface()
    {
        Result.SetActive(true);
        ResultText.text = "Game Over!";        
        
        // 停止所有
        Time.timeScale = 0;
    }

6.3  分数显示

将分数放在右上角,用Text控件实现

6.4  退出界面

将EscInterface放在Update()中,通过GetKeyDown检测Esc键是否按下。

    public GameObject Menu;
    [SerializeField] private bool MenuKeys = true;

    // Esc界面
    void EscInterface()
    {
        if (MenuKeys)
        {
            if (Input.GetKeyDown(KeyCode.Escape))
            {
                Menu.SetActive(true);
                MenuKeys = false;
                Time.timeScale = 0;
            }
        }
        else if (Input.GetKeyDown(KeyCode.Escape))
        {
            Menu.SetActive(false);
            MenuKeys = true;
            Time.timeScale = 1;
        }
    }

关闭程序可以用下行代码

    public void ExitGame()
    {
        Application.Quit();
    }

  • 5
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DayDayUp..

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值