前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一年前一篇关于学做unity游戏的草稿,好像是忘推送了

一年前一篇关于学做unity游戏的草稿,好像是忘推送了

作者头像
花叔
发布2023-12-14 08:48:01
1690
发布2023-12-14 08:48:01
举报
文章被收录于专栏:花叔的专栏花叔的专栏

今天登录发现有篇草稿是22写的,那回应该还是疫情...看完我又想动手做点什么了。

原文开始

“爸爸,我不要你工作!”

这是我在远程办公期间最常听见的一句话。

现在除了被会议和沟通占据时间外,家庭也要占据我不少时间和精力,但心里总觉得技术管理者还是需要懂点技术,不然就会没安全感。

但学习和复习技术就得花时间,这就形成了个矛盾点。

(配图源至:电影《负重前行》)

回忆过去,我们之所以能在某领域从业很大一个因素是因为热爱,对于热爱的事情,我觉得总能挤出些时间来,所以感觉还是能抽空去学一些自己喜欢的、新的东西。

三个月前了解到Unity和UE,也想学学,所以想试着在上述那种状态下,看能不能依靠业余时间做出一个Unity游戏,想到了就开干,就动手了。

按以前行文习惯,还是先看看成品,是一个益智类游戏,目的是通过连线攻占据点:

https://mpvideo.qpic.cn/0bc35maa4aaacuadxxdyh5svb26db3vqadqa.f10002.mp4?

跟以往不一样,这次有意在学习的过程中把整个思考历程记录下来,希望对自己以后有用,也希望能给看到本文的同学产生点正向影响,都是靠零散的时间进行编码和学习,内容不一定很系统甚至有点简陋,求轻虐。

(本文非常的长)

正文开始。

首先Unity是一个主流的游戏引擎,在学习他之前我有些事情可以稍微想清楚一下。

1.Why:

出于爱好,却可能跟工作结合,有性价比,对比UE,资源和文档丰富。

2.目标:

通过做出1款3d游戏学习和掌握unity引擎开发基础知识。

3.资源:

时间:

每晚下班后1-2小时

周末和假日不确定时间

设备:

一台闲置的iMac Pro

一台华为Mate40 Pro

人力:

只有我自己

4.SWOT:

(制图软件:在线版processon)

我理解SWOT是个策略制定工具,主要想把一些关键的行为方向给归纳出来,在真正立项的时候心中就会有基础的论调和方向。其中S (strengths)是优势、W (weaknesses)是劣势、O (opportunities)是机会、T (threats)是威胁,当四个点放在一个十字坐标体系中时,每个象限都能提出一个策略。

如果外部的机会正好是你的优势,赶紧利用起来。 而这个利用方法,就被称为“机会优势战略”(OS) 外部的机会但是是你的劣势,你就需要改进。 那么这个改进方法,就被称为“机会劣势战略”(OW) 你具有优势但是外部存在威胁,那就需要时刻盯梢、保持警惕。 而如何监视、监视哪些?就被称为“优势威胁战略”(ST) 既是威胁又是你的劣势,请及时逃离并消除。 这个消除方法,就是“威胁劣势战略”(TW) 知乎

5.既定节奏:

非常习惯先从大方向上定出区间时间内的节奏点,这样有利于自己知道目前处于什么阶段,这是自己认为基于Unity去开发一个游戏的节奏。

(制图软件:在线版processon)

弄清楚以上几点后,从“既定节奏”开始分布推进。

一.预研 预研目的是通过制作初步demo判断技术(能力)或客观环境是否存在不可逾越的瓶颈,如果存在,那么没必要过分高估自己,应该要及时放手(但这时别人不一定很好理解自己的做法);如果没有瓶颈,那么就拆分基础知识模块,然后分步骤进行学习,这个过程是启蒙阶段,是最容易放弃的时期,很多新的知识点会一拥而上。这时候,经验告诉我只要心中默念一点就好:任何一门技术只要是容易的,那么就没有学习意义。

回归正题,先从大的角度对Unity进行概览认知,他跟主流游戏引擎一样,给开发者提供的是:结合IDE提供自主框架和类库的整套制作方案。细分去看,要从三大块基础知识开始学:

代码语言:javascript
复制
IDE编辑器学习(包括资源获取)
C#语言学习
Unity类库学习

(Unity可视化编辑软件)

关于第一点IDE编辑器学习

其实也没什么太巧的学习方式,就是硬刷,先看一两遍官方介绍文档:

https://docs.unity3d.com/cn/2021.2/Manual/UnityOverview.html

Unity的学习资源还有很多,有些同学习惯看视频学也是OK的,在B站、油管搜一下基本上就有一大堆。

(图源:B站搜索页)

(图源:Youtube搜索页)

实际上,如果短期目的是做出一个游戏的话,手册或视频里很多点是可以先快速过一遍,不用太深究,先掌握大框架思路比较好,因为要深入的点太多了,如果被其中一块卡住了,就很容易陷入一叶障目的境地,会停滞不前。

不太懂没关系,回过头来有实际应用场景的时候再深究它,效率会高很多。所以视频教程我只看了油管上面的一个介绍基础界面的,就没再看了。

可以马上动手试试。

Unity对新手还是很友好的,除了有完善的学习社区外,还有附带教程的项目模板,能新建一个学习类项目,里面会以类似游戏新手教程的交互来一步步给你介绍相关的功能和特性:

(图源:Unity 乐高学习项目)

稍微跑一下这些学习项目后,就会发现其实Unity跟Cocos Creator一样,都是“组件式”的开发模式,一个物体节点上可以挂载系统自带的、自己自定义创建的组件,最后各类组件组合在一块就形成了一个游戏,那么编程语言就挺重要了。

关于第二点C#语言的学习

学新的计算机语言的话我是有现成优势的,毕竟基于已有的js、ts以及php的开发经验,很多语法、模式都是类似的,我很快就能上手,但想要更高效地编写代码的话,基础的事情得做,还是得研读一两遍C#的API手册:

https://www.w3cschool.cn/wkcsharp/3df41j39.html

同样,也不能在一些难懂的点上停留太多。不过一些关键逻辑还是需要弄清楚,基于以往研发游戏的经验,我很容易会总结出哪些功能块需要快速攻破:

延迟执行 网络请求 对象池技术 类和继承 数据序列化 ...

短期重点把这些常用功能的实现方式搞清楚并输出demo即可,带着对比的逻辑去学可能更快。

比如“网络请求”,在js中,前端同学是用Ajax的方法实现,基于XMLRequest,我们可以封装一个方法,并灵活地进行回调定义(当然用promise可能还更方便):

代码语言:javascript
复制
myAjax.req({
  type:"post",
  data:{value:1},
  success(e){
    console.log(e);
  }
})

而在C#中如果也要实现类似的逻辑,未来在大规模使用网络请求时,就能得心应手。那么该怎么实现呢,翻资料发现C#“协程”类比于js中的异步,而“委托”又能把“函数”变成变量,那么C#中用“协程”+“委托”就能实现“自定义回调”的逻辑:

代码语言:javascript
复制
using UnityEngine;
using UnityEngine.Networking;
public class JoRequest{
  public delegate void callback(string n);
  public static IEnumerator GetRequest(string uri, callback c){
      UnityWebRequest uwr = UnityWebRequest.Get(uri);
      yield return uwr.SendWebRequest();
      if (uwr.isNetworkError)
      {
          Debug.Log("Error While Sending: " + uwr.error);
      }
      else
      {
          c(uwr.downloadHandler.text);
      }
  }
}
代码语言:javascript
复制
void Start(){
    StartCoroutine(JoRequest
        .Get("https://cdn.wxnodes.cn/Fk/getAppInfo-tt-101.txt",
        result));
    //或者直接用匿名函数    
    StartCoroutine(JoRequest
        .Get("https://cdn.wxnodes.cn/Fk/getAppInfo-tt-101.txt",
        delegate (string x)
        {
            print("success:" + x);
        }));        
}
public void result(string s){
    print(s);
}

对“协程”(关键字IEnumerator, yield , StartCoroutine)理解还不是很深刻,但先不深究,因为目的是先把“灵活的网络请求”的通用方法实现,先继续往下跑,把其他关键功能模块也都实现一遍。

题外话,C#越写就越觉得有意思。

关于第三点Unity类库学习

对可视化为主的IDE、脚本语言有一定认识后,接下来就是相对来说更难的一个知识点的掌握了---Unity的类库。

为什么说它难,是因为它结合ide和脚本语言,把资源、代码、工程化糅和在一块,按照它的设计模式封装了很多组件和通用方案,这样要理解起来就需要联想和发散。而同样的,我们没法把所有点都深究,但有一些是必须清清楚楚搞懂的:

顶层对象 常用Component 框架层设计模式 动画 ...

类比cocos creator的cc.Node,Unity也有顶层对象,在脚本层面看就是GameObject这个类,任何在游戏中出现的元素,其实在脚本层面都指向一个GameObject对象,这个对象下面挂载了各类组件,最基础的就是Transform组件,通过它可以控制物体的位置、旋转角度、比例等基础信息。

(Unity编辑器 & VsCode)

万物皆物体和组件。

挂载在物体上的组件能实现各类效果,也能与其他物体的组件发生交互。

而实现这一切需要基于一个顶层的框架设计模式,Unity的脚本默认会继承MonoBehaviour这个通用类,他会使组件置于它的顶层框架模式中,里面会约定好组件在生命周期内的各种环节,并暴露相关回调方法:

代码语言:javascript
复制
Awake:当一个脚本被实例化时,Awake 被调用。我们大多在这个类中完成成员变量的初始化。
Start:仅在 Update 函数第一次被调用前调用。因为它是在 Awake 之后被调用的,我们可以把一些需要依赖 Awake 的变量放在Start里面初始化。协程发生地方。
Update:当开始播放游戏帧时(此时,GameObject 已实例化完毕),其 Update 在 每一帧 被调用。
LateUpdate:LateUpdate 是在所有 Update 函数调用后被调用。
FixedUpdate:当 MonoBehaviour 启用时,其 FixedUpdate 在每一固定帧被调用。
OnEnable:当对象变为可用或激活状态时此函数被调用。
OnDisable:当对象变为不可用或非激活状态时此函数被调用。
OnDestroy:当 MonoBehaviour 将被销毁时,这个函数被调用。

这理解起来很简单吧,其实Cocos Creator也是这种模式,所以对于我来说非常快就上手了。

除了对“顶层对象”、“框架设计模式”的理解,对常用组件(含动画)的基础理解也很重要,在Unity中,我觉得几类基础的Component还是需要先简单看看基础文档学习一下的,比如用于控制视角的摄像机(camera)组件、用于渲染物体的光照、模型、管线、材质、纹理、天空盒、粒子系统等组件、天空盒组件、用于仿真物理的碰撞、关节、材质等组件以及音频动画等组件,我们可以先攻克这些基础的组件,其实还可以了解很多进阶的组件以及商店中的第三方组件。

对于预研,上面几点做得差不多后,还得复习一些基础的几何数学知识,比如向量的处理(在Unity中重点要了解Vector类),加上一个基础的想法就能做出一个小demo,这里展示一下:

https://mpvideo.qpic.cn/0bc3gqaacaaaruaa7h6z3bqvangdae2aaaia.f10002.mp4?

(MAC自带截屏工具 & 手机剪映)

它实现星球表面移动物体的效果,原理是基于两次法向量求取,就能得出球面两个点之间的切线方向,进而能求出移动的路径。

(构图软件:photoshop)

里面处理涉及这种简单的数学处理外,

代码语言:javascript
复制
Vector3 b = Vector3.Cross(startPoint, goalPoint - startPoint);
Vector3 m = Vector3.Cross(b, startPoint).normalized / 20;
Vector3 newPos = startPoint + m;

更多涉及的Unity提供的资源引用、动画、事件控制、物理引擎等知识,这里就不展开说了。

当实现到这个程度的时候,我认为预研已经有了正面的结论,能继续往下做。

二.策划

游戏策划、产品设计专业显然不是我的专长,我翻阅了游戏策划工作相关的介绍,发现这工作可不是随便一下子就能胜任,中大型项目的策划工作细分下来还能分为:主策划、系统策划、数值策划、叙事策划...等等模块。

(图源:TiMi Club 天美俱乐部)

即使我做的是轻度休闲游戏,如果做的是一个完全自主创新的游戏,也可能需要主策划和数值策划,所以我要稍微切换到这个角色一下。

从工作流和所需能力看,对策划同学的要求非常高,本质上游戏策划岗位是一个门槛相对低,上限却特别高的岗位。

(图源:TiMi Club 天美俱乐部)

然而要做出一个游戏,这个环节又是必须的。但我不是在创业,我只是在学习,且只有我一人力,那么“游戏策划”我就没法花太多精力去深究,咋办呢?

那就做微创新吧,意思是基于一个现成的玩法去做差异化,这样能大大节省在“游戏策划”工作的投入比。

机缘巧合下,跟同事讨论过一个塔防攻占的玩法:地图上有不同颜色阵营的塔,每个塔有自身的数值,玩家可以控制蓝色阵营的塔,通过连线方式对其他阵营的塔进行派兵攻击,游戏最终目的是把所有塔变蓝。

大致的交互如下:

(制图软件:balsamiq mockups for mac)

简单调研后(其实也就是几个同事都觉得玩法还可以),我觉得可以给自己立项去做。所以就找思豪同学(经常一起劈酒的运营同事)要了个游戏策划文档模板,试着自己写了一份策划文档:

(图源软件:Mac Pages)

写着写着发现策划工作真不是我擅长的,要事无巨细地把设计概述、玩法体系、功能模块、道具说明、数值需求、美术需求等说清楚(甚至都把数据库表设计出来了),需要相当的耐心和细心,多人协助时还需要很强的沟通能力和项管能力,对于我这种白羊座老年人有点吃力。

扯远了,基于策划文档,我还需要画了个简单的交互稿(很重要,不然写代码的时候会总不知道自己下一步该往哪走):

(制图软件:balsamiq mockups for mac)

在常规项目中,我一般都建议产品经理或交互设计师用最直白的方式把交互稿画出来,甚至手绘都可以,我自己习惯用mac上的一个叫balsamiq mockups的软件,主要是喜欢它的画风,也喜欢它的轻便。

按我理解,交互稿里只要做到两点即可:1.把需要出现的元素呈现出来,2.把元素间的关系表达出来。

我习惯用线连来连去地把跳转逻辑说清楚,然后用适当的注解来表达一些逻辑。

当前策划的游戏虽然有些细节但并不复杂,涉及到两三个场景以及几个弹框,还有一些排行榜、商城等功能。

在交互稿和策划案有了后就可以开始规划工作了 --- 拆任务。

三.排期

项目排期一般是项目经理做的(有些厉害的产品同学也会兼顾去做),做的事情很简单,我理解大概有三块:1.分析交互稿和策划案;2.按职能拆分子任务,并呈现排期计划(比如输出排期甘特图),评估总周期;3.定期检验计划和进度。

(图源:due.com)

既然是一人项目,我也需要切换成项目经理的角色去做做这个事情。

对于第一点,因为就是我自己画的交互稿,我不用分析策划案了,评估下总研发周期和拆分下任务即可,考虑到目前工作情况结合之前的研发经验,大概可能需要1-2个月,具体计划用排期工具排一下:

(制图软件:OmniPlan3 for mac)

由于我一天大概只有下班后或周末的3小时左右时间来做,10天就是240小时,折算下来我要80天也就是接近3个月才能弄完这些。

对于一人力的排期来说,最重大的作用是形成拆分任务的队列,同时大概知道任务之间的串并行关系。而对于单个任务结束时间点的判断有点不大可控,因为在这个业余项目上我的有效把握时间变化幅度太大。

但这份表的作用还是很大的,我能清楚知道接着要干嘛。

四.执行

执行是一个枯燥的过程,考验的是耐心和体力,可能需要反复用到类似的方法,也可能反复试错某些逻辑。

对于我来说,有几个大的点是必须攻克的:

第一个是排期表里的各类开发工作

这块没多大的技巧,开发层面主要涉及到php开发、unity开发、数据库设计等,因为要存储数据需要用到服务器,逻辑服务器和数据库用的是腾讯云的。

(腾讯云数据库管理端)

提醒,没有一定后台开发或数据设计经验的前端同学可能要稍微学习一下基础的后台编程和数据库基础的增删改查知识,同时也需要了解一点运维知识。

然后接下来就是按照交互思路努力去实现客户端和服务端的逻辑。因为游戏的数据层比较简单,所以服务器侧的后台开发和数据库都不复杂,PHP用的是CI框架,数据库用的是MYSQL,涉及的库表没太多关联关系,记录用户过关信息时用了“多对多的关联表”。

第二个是游戏的视觉设计/资源采购

虽然我能自己做视觉设计,但因为不是太擅长,如果每个元素都要自己设计和制作,整个研发周期会拖得非常长,所以还是退而求之用“钱”解决吧。

但这种一人项目,有时候找一个资源或排一次版,我就能耗费1-2小时,而且很难做出眼前一亮的感觉,也还是需要稍微磨一下。

庆幸的是unity商店的资源挺丰富的,刚好赶上过年,商店在做虎年资源包活动,有两个39.9美元的资源包非常划算(非广告,现在已经没有了),我都买下来了。

(unity商店活动)

里面有一套完整的GUI素材,涵盖了首页、关卡页、各类弹框、按钮等素材:

也包含了一些系列模型:

除此外还包含了很多特效、动作等素材,利用这些素材,我能快速把框架搭起来。

第三个是实现通用网络请求模块

要游戏具备数据交互能力的话,需要把网络请求模块单独抽离并设计为一个可复用的公用模块,而鉴权机制是前后端数据交互的基础,是开发各类网络接口前必须首先解决的问题。

而由于是个简单的游戏,所以鉴权时序图逻辑不需要设计得太复杂,游戏提供两种登录方式。

一.微信登录

(制图软件:在线版processon)

用android下微信登录的sdk加上自定义的对称加解密方式去构造每次请求时的身份票据即可(上图中的joKey),有些细心的同学可能还会加上过期时间参数,每次校验票据是否合法时还会校验过期时间,我这里没做。

而对于微信登录sdk怎么用的问题,直接参考官方的文档即可(网上搜到的一些教程说不定不是最新的)

https://developers.weixin.qq.com/doc/oplatform/Mobile_App/Access_Guide/Android.html#jump

虽然注意的是,在android实现微信登录时,WXEntryActivity文件必须放在跟包名一致的文件目录下,不然即使在AndroidManifest.xml配置了activity,最后也不会拉起WXEntryActivity中的回调,这点相当的坑。

即使我在用cocos creator做游戏时做过类似的事情,但也经常无可避免地碰到一些自己都认为很低级却隐蔽的坑点,没办法有些时候就是反复被坑才能长记性。

(Unity编辑器截图)

(VScode编辑器截图)

二.注册登录

可能未来要发行到海外平台,所以我也顺便把“注册”和“登录”功能做了,这一块相对简单,就是按常规的账号+密码密文存储和比对的方式设计就好(不想搞太复杂,没做防刷),具体的登陆交互时序类比微信登录,只不过在换取openid的时候用的是自定义账号和密码。

最终实现这样的效果:https://mpvideo.qpic.cn/0bc37yacyaaaoeadbqqnwrrfb7wdft7aalaa.f10002.mp4?

(MAC自带截屏工具)

当登陆(鉴权)搞定后,后面所有的请求都能在一个权限范围内进行交互,剩下来的事情就是服务端实现各种数据处理逻辑,然后回传给客户端,这里需要有一定的后端开发和数据库设计能力,我一直都有PHP CI框架的后端编程能力,同时对mysql查询、数据库设计也略知一二,所以对我来说,这种简单的游戏后台并不难。

但对于没接触过类似知识的前端同学来说,用nodejs和nosql可能会更简单些。

第四个是“定义通用组件和方法”

提早抽重一些通用模块,这样后续研发效率会大大提升。

结合上述的鉴权逻辑,针对“请求”可以定义全局公用类,比如我会定义一个叫JoRequest的全局类,里面起码涵盖请求相关的三个通用方法:a.鉴权请求,b.匿名请求,c.图片请求(这是针对当前游戏交互认为要优先定义的,如果别的游戏,这里就要扩展更多通用方法了):

代码语言:javascript
复制
public class JoRequest : MonoBehaviour
{
    public static string root = "https://draw.wxnodes.cn/JoPlanetsAndroid/";
    public delegate void callback(string n);
    public delegate void imgCallback(Sprite n);
    public delegate void fail(string n);
    /// <summary>
    /// 带鉴权的请求
    /// </summary>
    /// <param name="action">对应远端对应的action</param>
    /// <param name="json">post过去的参数</param>
    /// <param name="c">成功回调</param>
    /// <param name="f">失败回调</param>
    /// <param name="t">MonoBehaviour实例,必须一定是实例化后的实例</param>
    /// <param name="code">微信登录的code或者自主登录的账号密码对,如果定义为Null也是带鉴权的请求</param>
    public JoRequest(
        string action,
        JObject json = null,
        callback c = null,
        fail f = null,
        MonoBehaviour t = null,
        string code = null)
    {
        //代码实现逻辑....
    }
    /// <summary>
    /// 不带带鉴权的请求,匿名请求,不带code参数
    /// </summary>
    /// <param name="action">对应远端对应的action</param>
    /// <param name="json">post过去的参数</param>
    /// <param name="c">成功回调,回传参数为string类</param>
    /// <param name="f">失败回调,回传参数为string类</param>
    /// <param name="t">MonoBehaviour实例,必须一定是实例化后的实例</param>
    public JoRequest(
        string action,
        JObject json = null,
        callback c = null,
        fail f = null,
        MonoBehaviour t = null)
    {
        //代码实现逻辑....
    }
    /// <summary>
    /// 加载图片,返回Sprite
    /// </summary>
    /// <param name="imgUrl">远端图片地址</param>
    /// <param name="c">成功回调,回传参数为Sprite类</param>
    /// <param name="t">MonoBehaviour实例,必须一定是实例化后的实例</param>
    public JoRequest(string imgUrl, imgCallback c, MonoBehaviour t = null)
    {
        //代码实现逻辑....
    }

另外,针对全局交互,还需要定义通用远端配置(用于未来动态配置)以及很多通用的方法,比如通用的模态弹框、缓存更改提取等,我会定义一个叫gbFun的全局类,然后在里面构建各种通用静态方法:

代码语言:javascript
复制
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using Newtonsoft.Json.Linq;
using Ricimi;
using UnityEngine;

public class gbFun : MonoBehaviour
{
    public static JObject gbAppInfo;
    /// <summary>
    /// 根据Resources/Popups里的预制,做的通用弹框方法
    /// </summary>
    /// <param name="name">预制文件名</param>
    /// <returns>GameObject</returns>
    public static GameObject pop(string name)
    {
        Canvas m_canvas = GameObject.Find("Canvas").GetComponent<Canvas>();
        GameObject popupPrefab = Resources.Load<GameObject>("Popups/" + name);
        var popup = Instantiate(popupPrefab) as GameObject;
        popup.SetActive(true);
        popup.transform.localScale = Vector3.zero;
        popup.transform.SetParent(m_canvas.transform, false);
        popup.GetComponent<Popup>().Open();
        return popup;
    }

    /// <summary>
    /// 保存用户数据到storage
    /// </summary>
    /// <param name="json"></param>
    public static void SaveUserDataStorage(string json)
    {
        //代码...
    }

    /// <summary>
    /// 获取storage里的用户数据
    /// </summary>
    /// <returns></returns>
    public static JObject getUserDataStorage()
    {
        //代码...
    }
    /// <summary>
    /// 把用户键值对保存到远端和本地
    /// </summary>
    /// <param name="t"></param>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <param name="c"></param>
    /// <param name="f"></param>
    public static void SaveUserDataValueRemote(
        MonoBehaviour t,
        string key,
        string value,
        JoRequest.callback c = null,
        JoRequest.callback f = null)
    {
        //代码...
    }
    /// <summary>
    /// 获取远端键值对用户数据,并刷新和保存本地数据
    /// </summary>
    /// <param name="t"></param>
    /// <param name="c"></param>
    /// <param name="f"></param>
    public static void getUserDataValueRemote(
        MonoBehaviour t,
        JoRequest.callback c,
        JoRequest.callback f = null)
    {
        //代码...
    }
    /// <summary>
    /// 保存键值对到本地缓存
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    public static void SaveStorage(string key, string value)
    {
         //代码...
    }
    /// <summary>
    /// 获取本地键值对数据
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public static string GetStorage(string key)
    {
        //代码...
    }


    public delegate void callback(string x = null);

    /// <summary>
    /// 前往关卡
    /// </summary>
    /// <param name="missionIndex">关卡序号id</param>
    /// <param name="t">一个实例化的MonoBehaviour</param>
    /// <param name="success">成功回调</param>
    /// <param name="fail">错误回调</param>
    public static void go(
        string missionIndex,
        MonoBehaviour t,
        callback success = null,
        callback fail = null)
    {
        //代码...
    }

    /// <summary>
    /// 根据当前关卡主键id,前往下一关的游戏页
    /// </summary>
    /// <param name="missionIndex">当前关卡的主键id</param>
    /// <param name="t">一个实例化的MonoBehaviour</param>
    /// <param name="success">成功回调,json字符</param>
    /// <param name="fail">失败回调</param>
    public static void goNext(
        string missionIndex,
        MonoBehaviour t,
        callback success = null,
        callback fail = null)
    {
        //代码...
    }
    /// <summary>
    /// pop的封装方法,模态弹框
    /// </summary>
    /// <param name="para">
    /// Hashtable参数
        // para.Add("text", "test");//弹框内容
        // para.Add("title", "title");//弹框标题
        // para.Add("commitText", "OK");//确认按钮文本
        // para.Add("cancelText", "CANCEL");//取消按钮文本
        // para.Add("ifCommitShut", false);//点击确认按钮时,是否需要直接关闭弹窗
        // para.Add("ifCancelShut", true);//点击取消按钮时,是否需要直接关闭弹窗
        // para.Add("needShut", false);//是否需要右上角显示关闭按钮
        // para.Add("needCancel", false);//是否需要显示取消按钮
        // para.Add("needSubmit", false);//是否需要显示确定按钮
        // para.Add("shut",(PopupBase.callback)delegate (GameObject a){ });//关闭按钮点击事件定义
        // para.Add("commit",(PopupBase.callback)delegate (GameObject a){ });//确认按钮点击事件定义
        // para.Add("cancel",(PopupBase.callback)delegate (GameObject a){ });//取消按钮点击事件定义
    /// </param>
    /// <returns>GameObject,弹框自身物体</returns>
    public static GameObject alert(Hashtable para)
    {
        //代码...
    }

    /// <summary>
    /// alert封装方法,用于简单的确认提示
    /// </summary>
    /// <param name="text">提示内容</param>
    /// <param name="title">提示标题,可选</param>
    /// <param name="submit">确认按钮点击事件,可选</param>
    /// <returns></returns>
    public static GameObject
    tips(string text, string title = null, PopupBase.callback submit = null)
    {
        //代码...
    }
}

这里强调通用类的建设主要的原因是“前置的顶层通用逻辑有利于后续模块复用和扩展”,虽然由于时间关系,不能在这里花太多心思去做细,但一定要有这么个顶层概念,不然后面很麻烦。

第五个是“性能优化”

任何一个游戏最后都要做性能优化,unity的性能优化可以单独作为一个课题了,涉及的知识太多,这里没法一一陈述。

通过优化overdraw的方式可以提升不少效果,而Unity编辑器很强大,在编辑视图就能实时看到每一帧的overdraw情况,就像观察一个热力图,一下子就能知道什么元素重绘得多,就能精准优化。

第五个是“执行和发布”

当游戏成型后,就要开始研究平台分发,而这里也是有技巧的,网上翻资料对于该技巧的描述还是挺少的,这里总结下。

在导出android apk时,如果要使用自定义资源或代码模板,那么就需要把自定义的代码文件和配置文件放在Assets/Plugins目录下,

同时在player setting中要勾上custom xxx用于指定使用自定义的配置文件。

这样能形成一个类似cocos creator中Build-template的模板,以后每次导出到apk时,自定义代码或资源就能带过去,这样就能非常便利地修改和编译,但需要注意的是:不是Assets/Plugins/Android下任何文件都能直接被带去编译目录,这里建议要多试试。

五.总结

平时写类似的文章,我可能只需两三个小时,但本文足足花了好几周时间,因为都是在边学习边做游戏的情况下一点点补充内容。这里纯记录了一次学习过程,如果你路过,有跟我一样的折腾习惯,可以留言交流哈。

原文结束

迟到的文章.....终于发了。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-12-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 MinProgram 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 MySQL
腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档