最近进行了几次面试,发现如今的Unity市场变得不太一样了,不再只是看重解决问题的能力,而是更注重面试表现,竞争也更加激烈。因此,在面试前应当互相明确以下几点:
1、面试岗位需求:确保了解岗位的具体要求和职责。
2、面试流程:确认面试有几轮,每一轮的侧重点是什么,特别是哪一轮是技术面试。(不然遇到非技术人员各自瞎问,后面面完才知道是跨服聊天,浪费双方时间)
以下是我的总结,希望对大家有所帮助:
基础类型的问题主要是一些概念性的问题,通常被称为经典八股文。一般来说,工作3到5年的人不会被问太多这类问题。不过,有时你可能会遇到一些非技术面试官,他们虽然不懂技术,但会质疑你的回答是否与网上标准答案一致。为了应对这种情况,可以提前总结和准备一些标准答案。
以下答案并不是标准答案,经供参考。
面向对象是对事物的抽象,在C#中处处是类,可以说处处都是(看做)对象。特点是继承、封装、多态。
继承是为了提高代码的复用性和扩展性,具体方法就是将子类相同的东西提取出来作为父类。
封装是为了保护代码不被轻易破坏,增强代码的安全性,同时也方便其她人使用,而在其她人使用的时候也不用去关心其内部逻辑是如何实现的。
多态放到最后说明其重要程度,继承和封装其实也是为多态准备。多态的定义是指同一接口、方法在不同的环境下有不同的表现(实现)。具体的体现是重写和重载,重写是指子类重写父类方法,就比如动物作为父类,其中都有一个声音的方法,小猫、小狗为子类重写父类的虚方法。
//该代码为AI生成 public class Animal { public virtual void MakeSound() { Debug.Log("Animal sound"); } } public class Dog : Animal { public override void MakeSound() { Debug.Log("Bark"); } } public class Cat : Animal { public override void MakeSound() { Debug.Log("Meow"); } }
重载是指同个方法名有不同参数,如果说重写是运行时的多态,那么重载就属于编译时的多态。
//该代码为AI生成 public class MathOperations { public int Add(int a, int b) { return a + b; } public double Add(double a, double b) { return a + b; } public int Add(int a, int b, int c) { return a + b + c; } }
当然,接口也能实现多态。
协程就是在unity主线程运行时开启另外一段逻辑来协助运行,来模拟多线程。使用场景多用于异步、动画、等待等操作,最常见的就是进度条异步加载。原理是利用了像list、Dictionary内部使用的迭代器,但是使用方法不同,不是迭代元素,而是分帧执行代码。每一帧调用迭代器的方法(MoveNext),其中unity利用关键字yield return快速实现了迭代器,两个yield return之间就是MoveNext方法执行的内容。
线程是操作系统调度的基本单位,一个程序至少会有一个线程。unity本身没有多线程,但是支持多线程的使用(不这样说,不懂的就查到网上很多文章说unity没有多线程的概念来质疑你)。unity中使用多线程是无法直接调用unity的api,需要访问unity的GameObject、Transform这些对象的时候可以搞容器去在主线程(比如在update)中处理。一般来说,多线程用在处理网络、数据计算、资源加载此类的场景。
List底层代码剖析
Dictionary底层代码剖析
首先是资源的加载、卸载,加载三部曲——按需加载、异步加载、及时卸载。
对象池的使用,大量物体重复创建、销毁会导致内存碎片和性能下降,使用对象池可以有效减少内存分配和GC。某些经常使用的游戏对象可以利用隐藏、显示的方式管理,不要随意使用创建和销毁。
减少内存废品,使用合适的数据结构,避免频繁分配以及释放内存,并进行初始化,避免不必要的内存浪费。
使用Profiler工具,查询并定位内存泄漏(会造成游戏卡顿,unity有一个Mono堆内存,可以想象成一个内存池,分配内存都从这里取用,释放也是归还给该池子,不够了就扩展池子,也就是池子只会不断变大)问题。
使用光照贴图、多层次细节(LOD)、静态合批和动态合批、减少模型面数和贴图大小等。
DrawCall是引擎或者说CPU(准备数据)通知GPU进行渲染的过程,一次通知就是1DrawCall。DrawCall过多会对显卡造成很大压力,我们需要适当降低她,方法一般为静态合批和动态合批,少用实时光照和阴影,UI同一层使用图集进行优化。
点乘 a b = |a||b|cos 判断前后方位
叉乘 a b = |a||b|sin 判断左右
略
在设计项目时,遵循SOLID原则,将核心功能分离成独立模块,每个模块负责单一功能,减少耦合。利用接口和抽象规范和约束各个模块,方便扩展和替换。利用各种设计模式,更高效的实现功能模块。
比如为了xx项目做了一个UI框架,包含UI的加载、打开、关闭等基础功能,为了项目还加入了反向切换、UI日志、多语言化等功能,简化开发流程,提高了开发效率。
单例模式:用于管理全局唯一实例,如游戏管理器(GameManager)。通过单例模式确保全局只有一个游戏管理器实例,方便管理游戏状态和全局资源。
观察者模式:观察者模式是一对多的依赖关系,一个对象发生改变,其他依赖者就会收到通知,通常用在消息推送、任务系统。有解耦。
工厂模式:用于创建复杂对象,如在武器系统中,根据不同类型生成相应的武器对象。通过工厂模式,简化了对象创建的逻辑,并提高了代码的扩展性。
命令模式:系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作,也可以考虑使用命令模式,在工业软件中常见,撤销和反撤销操作使用居多。
状态模式:其实就是FSM有限状态机的使用。
Unity状态机
分层、分治、演化思维。
分层是指将项目分为多个不同的层级,比较笼统的说就是UI框架层、资源管理层、数据层、网络层以及核心逻辑层。其中核心逻辑层按照项目进行规划,可能有地图场景和寻路、AI、工具层、着色器和特效、编辑器工具等。
分治是指把复杂的问题变成小的问题解决再组合起来。以Unity工作流举例,除了逻辑代码编辑、打包发布、版本控制、项目管理都能变成小的问题,将其解决,开发将会更加顺利。比如用gitlab+sourcetree进行版本控制,用Jenkins来进行打包发布,用Python或命令处理Excel数据等等。
演化思维是指设计项目时瞻前顾后,一个项目想长期经营下去需要不断的迭代更新,刚开始可能只有一两人的团队慢慢扩大,业务扩张,项目也随之进行迭代。像阿里就随着扩张渐进式的将单块架构(功能集中、易于开发、部署)转为微服务架构(分布式服务架构,将业务进行解耦)。
Unity读书系列《Unity高级编程:主程手记》——架构
做日志系统,分等级划分;
将玩家的操作和其他变化打上时间戳,变为数据;
定期和团队进行沟通,确保项目进度。根据团队成员的能力和兴趣分配工作,鼓励团队分享和学习知识,共同解决问题,提升凝聚力。
制作网络内容时,物联网部分,自己实现协议,出现粘包等问题。多学习相关知识,粘包问题有两种解决方法,一是将消息的首尾进行规定,二是在发送时就规定包长。
内存和性能优化,使用Profiler工具分析原因,使用对象池,UI的无限循环,优化资源加载策略等解决问题。
我想知道你作为面试官想让我问什么问题,如果回答不出来,我会很失望。此为绝杀,轻易勿用,觉得面的不行可以说,顺便向面试官了解一下自己(在对方眼中)的不足。
属于加分项,能说多少是多少吧。
面试前:
面试中:
我也是有自己的坚持的:理念不合的不去,不坦诚相待的不去,加班严重的不去。找到一家理念相符、尊重个人的公司很是重要。
回到北京几个月,发现非初级岗位少了不少。深圳、上海那边的视频面试结果还不错,机会也多,只是需要远途跋涉。目前我打算先在北京找找,如果实在不行再考虑其他地方。
最后,希望能找到一家理念合适,能长期合作进步的公司吧。
面试不是单纯的考试,而是一个双向选择的过程,是展示自我的机会,同时也是让公司了解你认同你的方式。在开始的几次面试中,我也是不断吸取经验,优化简历和自我调整。