第八章 C#脚本(上)

news2024/11/26 16:22:50

脚本是使用 Unity 开发的所有应用程序中必不可少的组成部分。大多数应用程序都需要脚本来响应玩家的输入并安排游戏过程中应发生的事件。游戏对象的行为由附加的组件控制。虽然Unity内置了许多组件,但是我们仍然可以使用脚本来创建自定义组件。Unity支持C#编程脚本语言(不熟悉的可以单独学习一下,看看我之前发布的C#课程),开发工具使用Visual Studio。我们一般情况下,在Unity中创建C#脚本,然后在VS中编写代码,最后回到Unity中运行。

创建C#脚本非常简单,需要我们在Project工程视图下创建,可以右击Project工程视图空白区选择“Create”-> “C# Script”,也可以通过菜单栏“Assets” -> “Create” - > “C# Script”来创建。新创建的脚本文件名称将处于选中状态,提示输入新名称。最好在此时输入新脚本的名称而不是稍后编辑名称。双击新创建的脚本文件,默认将使用 Visual Studio打开。新创建的脚本文件不是一个空白文件,而是一个继承“MonoBehaviour”的C#类。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Test : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

注意,类名和文件名必须相同,且该脚本必须附加到游戏对象上才能被运行。
这个MonoBehaviour 是Unity的一个内置类,该类中有两个方法:Start和Update。

我们知道游戏就是一个无限循环,每一次的循环都会接收和响应用户输入,渲染游戏对象(模型网格),播放游戏动画,特效声音或背景音乐等等。每一次的循环我们可以理解为一帧(一次渲染)。我们知道动画的原理就是通过一帧一帧的静态图片播放而实现的。为了能够让人眼感觉到这不是图片,而是动画的效果,1秒钟之内至少要播放24张连续图片才能达到最终的效果。游戏的本质也是如此,游戏引擎会在1秒钟之内循环渲染N次场景内的所有游戏对象,这个数值N就是我们所说的FPS(Frames Per Second)。FPS越大,游戏视觉效果越好。通常情况下,手机游戏的FPS=30就可以了,PC游戏的FPS=60就可以了,VR游戏的FPS=90就可以了。当然,如果我们的硬件允许的话,达到120的FPS是最棒的效果。这个话题我们不在细说了,回到MonoBehaviour中的Start和Update方法上来。Update 方法是放置代码的主要地方,用于处理游戏对象的帧更新(FPS值就是1秒钟update被执行了多少次)。这可能包括角色移动、响应用户输入,基本上涉及游戏运行过程中随时间推移而需要处理的任何事项。在处理游戏业务逻辑的时候,经常会使用附加到游戏对象身上的各种组件(游戏对象的大部分行为都是通过组件完成的),我们会通过组件来完成不同的功能,以及组件与组件之间的关联关系。为了使 Update 方法正常运行,在进行任何游戏操作之前,通常需要确保游戏对象身上的组件能够正确获取,相当于初始化操作。所以,在游戏开始之前(即第一次调用Update方法之前),Unity 将调用Start方法,此方法是进行游戏对象初始化的位置。我们可以在这个方法中来获取游戏对象上面的各种组件。

这里需要注意的,我们的C#脚本虽然是一个类,但是不能为其添加构造方法。这是因为C#脚本类对象的构造由Unity来实例化。如果尝试为脚本组件定义构造方法,将会干扰 Unity 的正常运行,并可能导致项目出现重大问题。如果想要在构造方法里面完成的代码逻辑,可以移植到Start方法内。

接下来,我们创建一个新工程(ScriptDemo),然后创建一个“Test.cs”脚本来测试一下Start和Update方法。创建文件成功后,文件就会自动处于重新命名的状态,我们只需要输出“Test”即可。输入完毕后,双击就会自动使用Visual Studio打开“Test.cs”脚本。

接下来,我们要做的就是添加如下的两行代码。

    // Start is called before the first frame update
    void Start()
    {
        Debug.Log("start method...");
    }

    // Update is called once per frame
    void Update()
    {
        Debug.Log("update method...");
    }

为了减少文档篇幅长度,我们省略了其他代码。

Debug.Log 是一个简单的命令,只是将消息输出到 Unity 的控制台Console面板。

由于默认创建的场景中只有“Main Camera”和“Directional Light”两个游戏对象,因此我们暂且将“Test.cs”的脚本文件附加到“Main Camera”游戏对象上面吧。这个操作非常简单,我们先在Hierarchy层次面板中选中“Main Camera”游戏对象,然后就会在Inspector检视面板中显示“Main Camera”的各种属性控件。最后我们将Project视图中的脚本文件拖动到“Main Camera”的Inspector检视视图上面即可。

接下来我们Play当前工程,应该会在 Unity Editor底部的Console 视图中看到此消息。

如果找不到Console 视图,可以在菜单栏Window -> General -> Console 重新打开。

我们发现,Start方法执行了一次,而Update方法则再不停的执行直到停止运行工程。在上面的截图中,我们会发现“Collapse”的按钮,它会重叠相同代码方式的输出。而重叠的次数则会在最右端以数字的形式展示处理。例如,上图中的update方法输出“update method...”被重复执行了94次。那么这个update方法一秒钟执行多少次呢(FPS值)?

我们可以使用代码来计算1秒钟内Update执行的次数来获取:

public class Test : MonoBehaviour
{
    private int count = 0;
    private float detaTime = 0.0f;

    void Update(){

	    //Debug.Log("update method...");
	
        // 累加帧数
        count++;
        // 累加时间
        detaTime += Time.deltaTime;

        // 累加时间超过1秒
        if (detaTime >= 1.0f)
        {
            // 计算FPS值
            float fps = count / detaTime;
            Debug.Log("FPS = " + fps);

            // 帧数和累加时间清零
            count = 0;
            detaTime = 0;
        }
    }
}

上面的代码逻辑非常简单,就是使用一个变量来累计Update方法执行的次数,同时累计当前执行的时间,Time.deltaTime 是Unity提供给我们的一个属性值,它代表当前帧和上一帧之间的时间差。因此,我们只需要累加这个属性值就可以获取update执行的时间长度detaTime。由于我们的代码运行都是毫秒级别的,所以当detaTime超过等于1秒钟的时候,我们就可以使用count/detaTime获取FPS值了。为什么不是detaTime等于1秒钟直接输出count值呢,因为这个detaTime不能非常非常精准的等于1,多数情况它的值可能会是1.001之类的,因此我们还需要使用count/detaTime做一个除法计算,得到一个包含小数的FPS值。

接下来,我们就Play当前工程,查看控制台视图输出。

我们发现这个FPS值并非是一个固定的数值,大概是在300-400之间(跟电脑配置有关)。

在Game窗口的右上角有一排按钮,其中有一个是“Stats”,我们点击一下就会出现一个新弹窗,在这个新弹窗上面,我们就也能看到FPS数值。

两者大致相同。既然FPS不是一个固定的数值,那么我们在上面提到过手机端(30),PC端(60)以及VR端(90)的FPS值,其实就是一个期望的平均值而已。我们工程中展示的FPS值是300-400之间,明显高于PC端的平均FPS值60啊?这么高的FPS值我们完全不需要啊(浪费电脑性能),我们如何设置其为60呢?这个问题稍微有些复杂。

首先,我们需要了解FPS值与显卡,显示器的关系?

显卡负责绘制图像再输送到显示器上,每个图像可以称为一帧。游戏帧数(FPS)就是显卡每秒绘制图像的数量。显示器则会将显卡绘制的图像显示到屏幕上。显示器刷新频率就是显示器画面每秒呈现图像的数量。一般情况下,我们电脑屏幕的刷新频率是60HZ,也就是说显示器1秒钟显示60张图像,当然我们也可以手动修改这个数值。那么,现在就存在一个问题,如果FPS值和显示器刷新频率不一样会出现什么情况?如果FPS高于显示器的刷新频率,那么多余的图像将不会被显示到屏幕上。因为动画的产生就是通过连续图像产生的,如果屏幕只显示一部分图像的话,那么这动画效果显然是不连贯的。如果FPS低于显示器的刷新频率的话,那么动画效果就会出现卡顿现象,达不到连贯效果。请注意,第一种情况是因为丢失图像而造成动画不连贯,后一种情况是显示太慢造成动画卡顿不连贯。想到这里,大家应该比较清楚了,最好的效果应该是让两个值大致相等,最好是FPS稍微大于显示器刷新频率,这样呈现的动画效果是最好的。为了解决这个问题,出现了垂直同步技术。开启垂直同步后,电脑就会等待上一张图像渲染完成后才会发出开始下一张渲染的命令,也就是说让显卡按照显示器的输出去绘制图像(强制游戏帧数最大不超过刷新率)。这样做唯一的缺点就是限制了显卡性能的发挥。但是,大家一定要明白的是,越高的FPS值越能让动画播放越流畅,给玩家非常棒的游戏体验,这一点是不变的。

在Unity中可以通过Application.targetFrameRate 指定的游戏使用的指定的FPS平均值。默认的targetFrameRate是 -1 数值,表示游戏应以平台的默认帧率渲染。

此默认速率取决于平台:

- 在独立平台上(例如PC),默认的帧率是可实现的最大帧率。

- 在移动平台上,默认帧率通常为每秒 30 帧。

- 在 WebGL 上,默认值允许浏览器选择帧率来匹配其渲染循环时序。

- 使用 VR 时,Unity 将使用 SDK 指定的目标帧率并忽略游戏指定的值。

看到上面的解释,我们就不难理解刚刚运行的工程的FPS是300-400之间了,因为PC平台的默认是能实现多大就实现多大,这个就跟个人电脑显卡档次有关系了。既然这样的话,我们就将Application.targetFrameRate 设置为 60,因为我的电脑显示器刷新频率就是60。

我们在“Test.cs”的脚本中的“Start”方法来设置FPS值,如下所示:

void Start()
{
	//Debug.Log("start method...");
	
    Application.targetFrameRate = 60;
}

再次Play我们的工程,查看控制台输出以及“Stats”弹窗。

 

我们发现两者大致也是相同的,都是近似等于60,这就说明我们设置起作用了。先不要着急,我们忽略了一个垂直同步技术。在Unity中我们可以通过QualitySettings.vSyncCount来设置是否开启垂直同步技术,0代表不开启,大于0代表开启。同时我们还可以点击菜单栏Edit->Project Setting->Quality,有一个VSync Count参数,该参数同样表示垂直同步。

它有三个值可以选择:Don’t Sync,Every V Blank和Every Second V Blank

Don't Sync:数值0,不设置垂直同步,通过Application.targetFrameRate来指定帧率

Every V Blank:数值1,帧率为60,Application.targetFrameRate无效

Every Second V Blank:数值2,帧率为30,Application.targetFrameRate无效

默认值是Every V Blank(数值1)。按照官方的解释,如果设置了 QualitySettings.vSyncCount 属性,将忽略targetFrameRate,而游戏将使用 vSyncCount 和平台的默认渲染率来确定目标帧率。上文中也给出了vSyncCount设置值与帧率的关系。另外,移动平台下这个vSyncCount不会起作用,而是使用targetFrameRate来设置帧率。还有VR平台下,vSyncCount也不会起作用,而是通过VR SDK来设置。这一点对于Unity开发游戏来讲,差距确实很大。但是当我们将VSync Count设置为Every Second V Blank的时候,FPS值没有改变。不知道到底是哪里的问题,貌似vSyncCount从开始就没有起作用,不讨论了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/471546.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用友T3提示,年度[UFDATA_002_2017]并不存在,导致无法备份迁移。

用友T3提示,年度[UFDATA_002_2017]并不存在,导致无法备份迁移。 现在都win11,迁移T3到新电脑,遇到了问题,某个年度账套不存在怎么办? 打开数据管理工具。无法导出。 002代表账套编号,2017代表…

【Zookeeper】 面试题总结

Zookeeper 1、工作中 Zookeeper 有什么用途吗2、zookeeper 数据模型是什么样的3、那你知道 znode 有几种类型呢4、你知道 znode 节点里面存储什么吗5、每个节点数据最大不能超过多少呢6、你知道 znode 节点上监听机制嘛7、那你讲下 Zookeeper 特性吧8、你刚提到顺序一致性&…

计算机网络-基础编程实验(JAVA\Python3)

计算机网络-网络基础编程实验(JAVA\Python3) 一.实验目的 通过本实验,学习采用Socket(套接字)设计简单的网络数据收发程序,理解应用数据包是如何通过传输层进行传送的。 二.实验内容 学习套接字编程,完成以下的网络数…

海尔牵头IEEE P2786国际标准通过Sponsor投票并连任工作组主席

01 海尔牵头IEEE P2786国际标准 通过Sponsor投票 并连任工作组主席 海尔牵头制定的全球首个服装物联网国际标准IEEE P2786《Standard for General Requirements and Interoperability for Internet of Clothing》通过Sponsor投票,标志着该国际标准草案得到了行业…

ThingsBoard教程(三六):规则节点解析 检查关系节点 check relation,实体类型节点 entity type

前言 本篇文章和大家一起来学习两个节点,检查关系节点 check relation和实体类型节点 entity type。 check relation 检查消息的发起者与其他实体之间是否存在关系。如果选择了“check relation to specific entity(检查与特定实体的关系)”,则必须指定相关实体。否则,…

程序员的4个真相(看了有点想乱搞)

最近在知乎看到一个很有意思的问题,热度还挺高的。 “程序员的通病是什么?” 很多人第一印象就是: - 加班 - 脱发 - 单身直男 - 亚历山大 - 需求魔幻 其实这些标签在其他行业领域也普遍存在,也并不是程序员真正的悲哀。 真正的…

3 程序流程控制-函数、包、常用函数【Go语言教程】

3 程序流程控制-函数、包【Go语言教程】 一、 程序流程控制 顺序控制分支控制switch分支for循环 Go 语言没有 while 和 do…while 语法,这一点需要同学们注意一下,如果我们需要使用类似其它语言(比如 java / c 的 while 和 do…while ),可以…

CVE-2023-28432 MiniO信息泄露漏洞复现

CVE-2023-28432 MiniO信息泄露漏洞 MiniO 是一个基于 Apache License v2.0 开源协议的对象存储服务。它兼容亚马逊 S3 云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等 在集群部署的 Minio…

计算机网络学习05(HTTP vs HTTPS)

1、HTTP 协议介绍 HTTP 协议,全称超文本传输协议(Hypertext Transfer Protocol)。顾名思义,HTTP 协议就是用来规范超文本的传输,超文本,也就是网络上的包括文本在内的各式各样的消息,具体来说&…

【干货分享】一文说透分布式一致性协议(上)

本文首发自「慕课网」,想了解更多IT干货内容,程序员圈内热闻,欢迎关注"慕课网"! 作者:大熊老师 | 慕课网讲师 在常见的分布式系统中,总会发生诸如机器宕机或网络异常(包括消息的延迟…

ChatGPT实现多语种翻译

语言翻译 多语种翻译是 NLP 领域的经典话题,也是过去很多 AI 研究的热门领域。一般来说,我们认为主流语种的互译一定程度上属于传统 AI 已经能较好完成的任务。比如谷歌翻译所采用的的神经机器翻译(NMT, Neural Machine Translation)技术就一度让世人惊…

【JavaEE进阶】——第四节.Spring更简单的实现Bean对象的存取(利用注解储存和注入Bean对象)

作者简介:大家好,我是未央; 博客首页:未央.303 系列专栏:JavaEE进阶 每日一句:人的一生,可以有所作为的时机只有一次,那就是现在!!! 文章目录 前…

Linux安装flutter

在Linux上安装flutter 1.使用快照安装flutter 安装snapd sudo apt update sudo apt install snapd安装flutter sudo snap install flutter --classic显示flutter sdk-path flutter sdk-path2.手动安装flutter 在flutter官网上查看安装流程 https://docs.flutter.dev/get…

现代微服务中缓存的分类及缓存一致性设计原则

引言 大部分面向公众的互联网系统,其并发请求数量与在线用户数量都是正相关的,而 MySQL能够承担的并发读写量是有一定上限的,当系统的访问量超过一定程度的时候,纯MySQL就很难应付了。 绝大多数互联网系统都是采用MySQLRedis这对…

MongoDB【索引-index】

目录 1:概述 2:索引的类型 2.1:单字段索引 2.2:复合索引 2.3:其他索引 3:索引的管理操作 3.1:索引的查看 3.2:索引的创建 3.3:索引的移除 4:索引的…

【福利赠书】有人说,测试驱动开发已死?(文末赠书3本)

友情推荐一本测试领域的教科书:(文末送3本) 《 测试驱动开发:入门、实战与进阶》,英文原书名为《Learning Test-Driven Development 》,是一本实用且有趣的TDD实践教程。如果你想开始做测试驱动开发&#x…

【WPF动画】简单构造一个相册轮播图淡化切换特效

效果图 简单的定时器结合DoubleAnimation使用示例&#xff0c;实现轮播图淡化切入特效 代码部分 <Image x:Name"carouselImage" Margin"10" HorizontalAlignment"Center" VerticalAlignment"Center" Stretch"UniformToFill&…

倾斜摄影超大场景的三维模型的顶层合并的纹理压缩与抽稀处理技术分析

倾斜摄影超大场景的三维模型的顶层合并的纹理压缩与抽稀处理技术分析 倾斜摄影超大场景的三维模型的顶层合并需要对纹理进行压缩和抽稀处理&#xff0c;以减小数据量和提高数据的传输和展示性能。以下是一种常用的纹理压缩和抽稀处理技术&#xff1a; 1、纹理图集 纹理瓦片化…

网络安全漏洞分析之远程代码执行

介绍 Apache Flume 是一个分布式的&#xff0c;可靠的&#xff0c;并且可用于高效地收集&#xff0c;汇总和移动大量日志数据的软件。它具有基于流数据流的简单而灵活的体系结构。它具有可调的可靠性机制以及许多故障转移和恢复机制&#xff0c;并且具有健壮性和容错性。它使用…

国民技术N32G430开发笔记(9)- IAP升级 Bootloader的制作

IAP升级 Bootloader的制作 1、上节提到Flash的分区&#xff0c;0x8000000-0x8004000为Boot分区&#xff0c;我们的bootloader就烧录到此分区。 Bootloader很简单&#xff0c;新建一个普通的工程&#xff0c; 也不用初始化外部设备&#xff0c;开机后&#xff0c;直接跳转到 A…