极简入门:从Unity到Asp .net core!
本文是写给Unity客户端开发从业人员的,用于开阔视野,提高自身姿势水平的科普向入门文 。
对于Unity开发从业人员来说,会写C#可以说是必备的技能了,那么除了游戏,我们的C#技能还能不能用在更多的地方呢?当然可以,如图:
在DotNet官网上,我们可以看到如上图所示,C#正在慢慢变得热闹起来,从原来的Windows PC独占语言,变成啥事都可以插上一脚的新“全栈”语言。而我们本文要介绍的Asp .Net Core,就是C#最擅长的,也是对于Unity程序员来说很容易上手的一个领域——Web开发.
重新认识C# 和 .Net
对于Unity开发者来说,我们认识的C#,通常就是Unity的一个写逻辑的东西,对C#的本身了解的可能并不是很全面,而对于.NET是个啥,可能压根就没印象。所以文章开篇,我们得花点小篇幅来简单介绍一下。
C#,其主要作者为Anders Hejlsberg,这人是个神仙,专门搞编程语言,做一个火一个:是Turbo Pascal编译器的主要作者,也是Delphi的主要作者,是C#和TypeScript之父,同时还是.NET平台的创立者。
这些语言相信大家都是耳熟能详了,尤其TypeScript,最近几乎占领了前端界的半壁江山,搞得JavaScript忠实爱好者们怨声载道。
在大多数情况下,C#编写的代码在使用时,并不是像C++一样被直接编译成可以被CPU执行的机器码,而是编译成另一种语言:IL语言(Microsoft Intermediate Language 微软中间语言)。
除了C#,.NET家族的其他语言,如F#、VB .NET等等,通常也会被编译成IL语言。除此之外,包括C++、Lua在内的很多“非微软血统”的语言,其实也有编译成IL语言的实现,不过这是另外的故事了。
当C#被编译成IL语言之后,CPU还是不认识它啊,怎么运行呢。这时候就是我们的.NET Framework来干活了, .NET Framework中包含了运行IL语言的运行环境.
所以C#实际被使用起来应该是这样的:C#先编译成IL,然后.NET Framework 来运行IL.
(其实这玩意叫CLR,但是因为是科普,不细说,先都认为是.NET Framework就完事了,有兴趣的朋友可以自行学习)
那么这时候,问题就来了。我们知道,.NET Framework是Windows系统独占的,它只能在Windows设备上安装和运行。这也就意味着,C#也成了Windows系统的专属语言。
实际上,相当长的岁月里,真的就一直都是这样的。这也是为什么C#没有Java火的历史原因之一。
但是,也不完全是这样。我们知道,在程序员圈子里有个很奇怪但又很厉害的存在,叫开源社区。
历史上有很多人觉得,C#这语言挺棒的啊,但是只能用在Windows上,太可惜了,要是能跑在其他地方就好了。当有这样的想法的人多了,这事也就成了,于是,Mono诞生了。
对,就是大家在Unity里看到的那个Mono.
Mono是对.NET Framework的第三方开源实现,它的一大特点就是跨平台。它可以让我们的 C#代码运行在包括Windows/Linux/OSX/Android/iOS和游戏主机在内的各种琳琅满目的平台上。
基于Mono也诞生了很多出色的东西,比如用于开发Android、iOS和Mac平台App的Xamarin(它两其实还挺有渊源的),还有建立在Xamarin基础上的另一个游戏引擎MonoGame.
而得益于Mono,很多不是C#开发的游戏引擎,也可以通过mono来使用C#开发游戏。著名的就比如Unity,底层的C++开发的,外面跑一层mono,然后在mono上跑业务代码。著名引擎Unreal Engine其实也有"mono-ue"和"USharp"这两个C#插件,此外还有卡普空、科乐美等一些公司私有的引擎也在这么使用C#(通过外包渠道见过,毕竟没公开,不细说)
(因为mono是对.NET Framework的第三方实现嘛,所以其实Unity也可以用F#来写逻辑的哦,并且真有人这么干过)
(顺便我们也可以很容易的理解Unity热更新方案 ILRuntime的原理了:它从dll中读取IL语言,然后直接像脚本一样解释运行)
再后来啊,也就在几年前,记不太清,大概是2015年前后吧,微软几经换帅之后,性情大变(不):“我要拥抱开源!”
本以为是说着玩玩的,过两年就没动静的那种,但没想到,微软这次是来真的了,重构狂魔微软给大家带来了一个新的东西:.NET Core.
.NET Core从某种角度来看,可以认为是.NET Framework的一个重新实现,是一个跨平台版本的.NET Framework,是一个官方版本的mono. 而且,.net core直接就基于MIT协议给开源了.
C#这个本来是由于历史原因 市场份额被Java吊着打的语言,从 .NET Core开始,重新回到了大众的视野中,而到 .NET Core 2.0的时候, C#可以说是又活过来了。到了今天,无论是性能排行[1]还是口碑的排行[2],都是走在前列了。
而我们的本文的主角,Asp .Net Core, (我要再不提你们是不是都快忘了),它就是建立在.NET Core基础上的一个很厉害的Web框架。
关于Asp
说到ASP, 其实不少人多多少少都对它有点“童年的记忆”,而且由于一些机构的只有IE才能打开的老旧网站,很多人一听到ASP这三个字,都有点不太好的印象。
但实际上,我们今天要说的这个Asp .net core,是个很年轻的新东西。历史上微软折腾过很多关于Web的技术方案,但不知道怎么想的,除了一个蓝泽光[3],其他的东西都起名字叫“ASP”了。不关注的人乍一看可乱了
但不管之前的“Asp”们,我们今天说的这个“ASP .NET Core”可是个非常年轻,非常现代化,非常强大的框架了。
在ASP .NET Core里,并没有像往常一样充满了微软自己家发明的私有轮子和概念,你可以在里面看到非常标准的mvc、mvvm,可以看到websocket,可以看到Google家的protobuf和gRPC,是个非常“开放”的的框架。asp .net core同样也是开源的,基于apache-2.0开源协议,和MIT协议的.NET Core一样,都是很常见很正常很宽松的正儿八经的开源,而不像某家把源码放出来之后只能看不能动,照样收钱(喂!
我们说Asp .Net Core是个web框架,那么什么是web框架呢?废话嘛,做网站的呗。
Asp .net core可以做传统的服务端渲染页面的mvc/mvvm网站;也可以配合angular/vue/blazor等“现代”前端框架搞目前挺流行的前后端分离;还可以通过websocket等技术[4]与客户端建立双工通讯的长连接,实现更多奇妙功能。可以说除了一些对实时性要求较高的应用(比如moba之类的游戏),asp .net core都可以很容易的胜任。(实不相瞒,我拿asp .net core写过棋牌游戏服务器)
并且,Asp .net core很完善,给的东西很全。比如有开发效率超高的数据库工具EF Core,有简单接入就能用的身份验证和授权系统,有开箱即用适配良好的gRPC,有第一方的容器化和微服务支持,还有几行代码就能写出一个分布式服务集群的大宝贝……
试用Asp .Net Core
闲话少说,书归正传。我们直接拿asp .net core出来看看它长啥样吧。
首先,我们需要下载.net core的sdk, 在官网 https://dot.net 直接下载就有了
然后,我们准备一个IDE,比如Visual Studio (or vs for mac) 或者 Jetbrains Rider, 懒得去下载IDE的话我们也可以使用万能神器 visual studio code.
然后,就可以新建工程了,在IDE中,我们可以直接创建:
如果使用VSCode的话,我们还可以通过命令行工具来创建(这个工具在.net core中很常用哦)
在这里,我们创建一个webapi项目。我们可以看到如下的目录结构:
首先我们来看Program.cs
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace HelloMeowApi
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
这儿是我们整个程序的入口。对于一开始就玩Unity来学编程的同学来说,这个可能是第一次见,Unity是没有专门入口的,你把程序写成组件挂在场景里,它就跟随场景一起呗被执行了。
但是在Asp .net core程序中,它有一个明确的入口告诉系统,程序从这里开始运行。(asp .net core本质上是个命令行程序)
在上面的代码中,我们的程序创建了一个“WebHost”,并给它配置了一个"Startup"类,我们先不管Startup类,我们先来看看Controllers.
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
namespace HelloMeowApi.Controllers
{ [ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
} [HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
}
}
默认的模板给了我们一个叫WeatherForecast的Controller,这里就是处理Api具体逻辑的地方,
首先,开头的[ApiController]告诉框架,我们这个类是个ApiController,
紧跟其后的[Route("[controller]")]告诉框架,我们这个controller可以通过什么路径访问到,而字符串"[controller]"这个配置是一个模板,指代我们这个Controller的名字(也就是类名)。也就是说,通过这行代码告诉框架,别人通过"http://localhost/WeatherForecast"这个Url就能访问到我们的这个Controller了。
我们把代码运行起来,IDE直接点运行按钮,也可以用命令行输入"dotnet run"来运行,然后在浏览器里访问"/WeatherForecast" (大小写不敏感)
我们看到我们的api服务器向我们返回了一传json文件,仔细一看是个json数组。(json格式是经过浏览器插件美化过的,实际返回的是挤在一起的那种)
这段代码在哪实现的呢,在这:
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
这是一段很"C#风格"的代码,对于一些Unity开发者来说,我们看着可能有点陌生:代码使用了LINQ (Language Integrated Query)[5],听名字就知道,它是查数据用的。但是我们做游戏的通常业务代码都在lua里面,所以可能对Linq并不常见。
如果把它替换成我们常见的写法,大概应该是这样:
那么这段代码就稍微好理解一点了,
首先,[HttpGet]告诉框架,这个方法用于处理使用GET方法[6]访问我们这个Controller的请求。这个方法内部创建了一堆假数据,并返回。
(C#的数组、List什么的都实现了IEnumerable<T>这个接口,它是更底层一点的东西,我们可以粗略的理解为这就是个数组)
框架接收到我们返回的一堆WeatherForecast类型的“数组”(假数据)之后,会在底层把它序列化成json文件(除了json,也可以变成别的东西),发给请求来的客户端(浏览器)。如此一来,一个HTTP(Restful API)请求就完成了。
这样一来,我们也算是大概了解了怎么用Asp .Net Core写一个Api服务器应用了吧。
这其中,涉及到了几个我们平时写游戏客户端的时候可能并不常见或者并不会在意的东西,这里需要了解了解:
首先是Linq,这个在.NET Core中很常用,当配合EF Core来操作数据库的时候,会大量的使用Linq.
然后是Web的一些基础概念,比如HTTP请求方法 、HTTP状态码 、content-type 等等,
还有一个目前好像看起来不是很明显,但很有用的概念: 依赖注入和控制反转 ,这个也是因为平时做游戏都在写Lua,好像用不到它。(但其实也有在游戏里使用它的,比如TinaX和QFramework都利用CatLib在Unity中实现了依赖注入的功能)
后续要学点啥呢
看完了一个例子,嗯好像会了,嗯好像挺简单的,那做点啥试试看呗。
欸,不会了。通过上面的例子,我们是知道怎么处理客户端的http请求了,但是好像还不够,真实的应用中没有这么简单的东西的,那么我们好像还得继续往下学习学习,都需要学点什么呢?
先定一个小目标:最快能做一个网页出来
Nuget 包管理:这是.net中常见的包管理方式,但很遗憾Unity默认并不支持,所以很多Unity开发者可能没见过Nuget
C#异步编程:在Unity中我们常用的异步编程方式是协程,无论在lua还是c#中写起来就是各种回调满天飞。而在C# 5.0开始,C#给我们提供了一种新的异步编程方式 ,这在.net core程序中几乎是必备的(当然,Unity也开始有人用了,比如TinaX就是这种异步写法)
数据库,你总得保存点数据吧. 配套.NET的官方ORM框架是Entity Framework Core (EF Core) ,数据库入门可选Mysql、MaraDB、MS SQL、PostgreSql等等,挑一个顺眼的先玩起来。
模板引擎,如果想要让web应用在服务端渲染页面的话,我们就需要一个asp .net core的模板引擎,它是Razor Pages
身份验证和授权,如果你想要你的网站可以有个用户登录注册、权限分配的功能的话,就需要身份验证和授权系统了。很棒的是,Asp .Net Core都帮我们准备好了:Asp .net core identity
而如果我们还想要做一个前后端分离的程序的话,比如使用Asp .net core写API服务器,用vue写前端、用flutter写客户端,甚至是Unity的游戏客户端,这样的话,我们需要在上述的基础上,学习这么些东西
身份验证和授权,之前我们做网页时候直接把用户什么的给Asp .net core管的,但是对于Api应用来说可就不能这样了,这里我们需要掌握一点新的概念 OAuth2 和 OpenID Connect 这两个不是.NET的东西,它两是行业通用概念,我们平时看到的“使用QQ号登录xxx”的那种第三方登录,就是基于这些理念去搞的。(预警:这两个概念第一遍看的时候会很头疼,多看两边就懂了)
Postman : 一个工具,GitHub家的,专门用来开发api时候用的
Swagger/OpenAPI : 生成API文档的东西,非常常用。
跨域请求 原理和使用
基本上,这些东西我们大概掌握了之后,我们就算是一个崭新的web开发人员啦,然后我们就可以很自豪的说:我又点出了新的技能,除了游戏客户端,我还可以写服务器啦。
当然啊,学无止境,如果我们有兴趣的话,还可以继续往下深入学习哦:
NoSql:另一种最近火起来的数据库形式,代表有MongoDB
Redis:另二种数据库,其实也是NoSql,它的特点是数据都放在内存里,通常当缓存用的。
GraphQL: 一种专门用于API查询的语言,现在慢慢的开始流行开了
gRPC:Google家的远程过程调用(RPC)框架,有的游戏里会用的protobuf其实就和它有点关系。
验证和加密相关:HTTPS原理啊、SSL/TLS原理啊等等
消息队列:代表有RabbitMQ、Kafka等等
容器化技术:直接搜docker和k8s,看两遍就知道是啥了
微服务概念:其实也是和容器化有点关系的东西,一起看看就会了
WebSocket:在Web的基础上实现实时双工通讯的技术,现在做html游戏必备,有的做Unity游戏的公司也会用
……
参考
^数据来自 techempower https://www.techempower.com/benchmarks/
^参考stackoverflow survey 2020数据 https://insights.stackoverflow.com/survey/2020#technology-most-loved-dreaded-and-wanted-web-frameworks
^萌娘百科 - 蓝泽光 https://zh.moegirl.org/%E8%93%9D%E6%B3%BD%E5%85%89
^SignalR https://docs.microsoft.com/zh-cn/aspnet/core/signalr/introduction?view=aspnetcore-3.1
^Linq是搞.net core很常用的东西,尤其是有数据库的时候,得好好学学 https://docs.microsoft.com/zh-cn/dotnet/csharp/tutorials/working-with-linq
^知识点 - HTTP请求方法 https://www.runoob.com/http/http-methods.html