在Unity上用IMGUI写个贪吃蛇

在Unity上用IMGUI写个贪吃蛇我为啥要用Unity的IMGUI做个蛇游:( 一个闲得蛋疼的程序员能有多无聊? 小白看不懂,老鸟皆来嘲!

前排叠甲:不要用IMGUI(即时模式GUI)制作游戏!!!(此文纯属个人瞎琢磨+神秘力量作用)

Unity官方文档如是说:

The IMGUI system is not generally intended to be used for normal in-game user interfaces that players might use and interact with.

IMGUI is a code-driven GUI system, and is mainly intended as a tool for programmers.

IMGUI是代码驱动的主要面向程序员的工具,一般不用于正常游戏内的玩家交互界面。与之相对应的,Unity有一套基于游戏对象的UI逻辑,此处就不赘述了。

叠甲结束,正文开始。

IMGUI基础

要使用IMGUI,需要在C#脚本中继承MonoBehaviour类,并且编写OnGUI方法,就像这样:

public class Example : MonoBehaviour {
    void OnGUI() {
        if (GUILayout.Button("Press Me"))
            Debug.Log("Hello!");
    }
}

现在来学习一下:OnGUI方法会在游戏循环的渲染过程中,场景渲染之后被调用,它的效果是在正常的画面之上覆盖一层GUI,因此它是显示在画面最上层的,不必考虑被其他元素覆盖的问题。同时注意到OnGUI方法是在游戏循环中与渲染被一起调用的,因此每帧画面OnGUI都会被调用一次(这和Update方法是一样的)。

看到if语句里的GUILayout.Button了吗?它的作用是在屏幕上绘制一个按钮,在IMGUI中的所有组件都是通过这样的方式进行绘制的。括号内的字符串则是我们要在按钮上显示的文字信息,而GUILayout.Button方法将会返回一个布尔值,它代表了组件的某种状态,比如当我们按下鼠标时,Button方法将会返回true,从而进入if方法的内部逻辑。当然,除了Button还有其他的GUI组件任君挑选,这里也不展开了,详见☛Unity官方文档(GUI)☚。

值得一提的是,由于每个循环OnGUI方法都会被调用一次,这意味着即使这个GUI组件只是一个局部变量,在它完成它的渲染使命,离开OnGUI方法后就会被销毁并消失,但下一次调用OnGUI方法时又会在同样的位置创建出一个新的组件,在使用者的眼里,它仿佛就一直待在那里,正因如此,我们不需要在类中显式地定义GUI组件成员

拓展:这里使用的是GUILayout的Button方法,GUILayout的组件一种自动布局组件,它不需要我们声明组件的绘制位置,这对于需要快速简单的游戏内Debug界面的程序员来说是一个不错的解决方案,详见☛Unity官方文档(GUILayout)☚。

在写好C#脚本后。还需要将脚本挂载到某个场景物体上,让游戏能够调用它,这个物体可以随意选择,一般选择一个方便定位的物体会比较好。

好了,想必有经验的程序员到这里已经明白怎么写出一款贪吃蛇游戏了吧~

游戏逻辑

状态图:

stateDiagram-v2
[*] --> Start
Start --> Init
Init --> GameLoop
GameLoop --> GameLoop
GameLoop --> Over
Paused --> GameLoop
GameLoop --> Paused
Over --> Init
Over --> [*]
  • Start状态需要完成如背景图片、蛇的图片等美术资源的加载
  • Init状态需要完成全局变量等游戏逻辑所依赖的变量的初始化
  • GameLoop状态则是游戏进行中
  • Paused状态下游戏暂停
  • Over状态游戏结束

代码实现

代码实现这一部分属于是懂的不屑于看,看的都不懂(开玩笑的),这里只提一下两个关键算法:

  • 蛇的移动
  • 果子生成

蛇的移动实际上指身体各个部位怎么移动,从物理的角度看,身体各个部分的移动方向只受其相邻“前面”的身体部位的移动方向的影响,具体而言有以下递推式(drct[i]表示第i截身体的移动方向):

drct[0] = 键盘输入方向

drct[i] = drct[i-1]

不需要多复杂的方法,直接模拟即可,需要使用结构体同时记录身体各部分的坐标以及方向:

struct body {
    int x, y, direction;
};

并且将所有的身体按照从头到尾的顺序放在数组中,在每次移动时都从头到尾遍历此数组,每次先对当前的一小截身体进行移动,然后将移动的方向向数组尾部传导:

void NextMove(body[] bodies, int bodyLength) {
    Move(bodies[0]);    //先移动头
    for(int i=1; i<bodyLength; i++) {
        Move(bodies[i]);
        bodies[i].direction = bodies[i-1].direction;
    }
}

果子生成实际上是要找到任意一个这样的位置:它上面没有任何的阻挡,且在我们限制的场地范围内。这实际上十分容易实现,只需要在 [ 1, 场地行数×场地列数 – 被占用的格子数 ] 范围内生成一个随机数R,然后按照某种顺序遍历整个场地,在统计到第R个空的格子时即可放置果子:

void GenerateFruit(int[,] gridState, int rows, int columns, ref int occupied) {
    int R = Random.Range(1, rows*columns - occupied);
    int i, j, count = 0;
    for(i=0; i<rows; i++) {
        for(j=0; j<columns; j++) {
            if(gridState[i][j] == 0)
                count++;
            if(count == R) {
                gridState[i, j] = 1;
                occupied++;
                break;
            }
        }
    }
}

最后提一嘴如何实现每秒移动一次:我们需要用到Unity提供的Time.deltaTime,它的值是上一帧到这一帧之间经过的时间,我们只需要一个静态成员累计经过的时间,在累计时间超过1s后将其减去1s并执行移动函数即可:

public class Example : MonoBehaviour {
    private static float totalTime = 0.0f;
    private static float waitTime = 1.0f;
    //...
    void OnGUI() {
        totalTime += Time.deltaTime;
        if (totalTime >= waitTime) {
            totalTime -= waitTime;
            //TODO
        }
        //...
    }
    //...
}

除了以上提到的这些,还可以加入积分等来丰富游戏内容。

效果预览

最后的效果(背景图使用Stable Diffusion生成): 媒体播放器 2023_9_29 20_22_31 (2).png

– fin –

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/36708.html

(0)
上一篇 2023-11-14
下一篇 2023-11-15

相关推荐

  • mysql 图片_MySQL 2003

    mysql 图片_MySQL 2003之前两篇文章带你了解了 MySQL 的基础语法和 MySQL 的进阶内容,那么这篇文章我们来了解一下 MySQL 中的高级内容。 本文思维导图如下。 我们知道,MyISAM 和 MEMORY 存储引擎支持表级锁定(table-level locking),InnoDB 存储引擎…

    2023-04-02
    89
  • 【Rust 实战】 贪吃蛇(控制台版)

    【Rust 实战】 贪吃蛇(控制台版)如果你已经看完了前面的 Rust 所有文章,那基本已经可以上手一些项目了。本篇文章也算是对前面知识的一种运用吧,我们一起来实现一个简单的贪吃蛇游戏(代码不到270行)。先看下效果图吧。

    2023-11-13
    79
  • MySQL 事务学习总结

    MySQL 事务学习总结MySQL 事务学习总结

    2023-05-22
    122
  • 接口设计(系统接口设计怎么写)

    接口设计(系统接口设计怎么写)

    2023-10-22
    78
  • 滴滴内部分享:如何提高代码的可读性,学习笔记

    滴滴内部分享:如何提高代码的可读性,学习笔记本文整理自 taowen 师傅在滴滴内部的分享。1.Why对一线开发人员来说,每天工作内容大多是在已有项目的基础上继续堆代码。当项目实在堆不动时

    2022-12-14
    86
  • 数据库常见面试题及答案_数据库面试题基础知识

    数据库常见面试题及答案_数据库面试题基础知识1.事务四大特性原子性,要么执行,要么不执行隔离性,所有操作全部执行完以前,其它会话不能看到过程一致性,事务前后,数据总额一致持久性,一旦事务提交,对数据的改变就是永久的2.数据库隔离级别,每个级别会引发什么问题,mysql默认是哪个级别脏读:事务B读取事务A还没有提交的数据不可重复读:两次事务读的数据不一致幻读:事务A修改了数据,事务B也修改了数据,这时在事务A看…

    2023-04-02
    102
  • 数学笔记怎么做比较好_板书主板书写在哪一边

    数学笔记怎么做比较好_板书主板书写在哪一边数学课上,全程键盘手打1700页笔记。请看下图:不仅排版媲美教科书,而且还能够批注,检索关键词…笔记被他Po到网上之后,便引来大量围观。

    2022-12-14
    91
  • 面试官:MySQL事务是怎么实现的「终于解决」

    面试官:MySQL事务是怎么实现的「终于解决」前言用过MySQL的同学都知道,它的InnoDB存储引擎,是通过事务来保证数据的一致性的。数据库事务通常包含了一个序列的对数据库的读/写操作。包含有以下两个目的:为数据库操作序列提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法。当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰。特性说到事务…

    2023-04-02
    77

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注