【2023-9】U3D面试经验

目录

LP科技:

1.说说Ngui和Ugui的区别×

2.xlua如何热更新、热补丁×

3.谈谈mvc与mvx的思想框架×

4.unity如何导航寻路

1.AB包的上传与下载

2.C#调用文件夹、文件流

3.Scriptable

4.InputSystem新输入系统

5.替身系统avater

6.三大可持续化

7.自动锁敌

8.Where约束

9.Lua

10.Xlua和Tolua区别

11.开闭包??


LP科技:

1.说说Ngui和Ugui的区别×

1.UGUI界面展示是在画布下(Canvas),而NGUI是在UIRoot下

2.UGUI继承RectTransform,RectTransform继承Transform,而Ngui直接继承Transform

3.UGUI没有图集Atlas,是直接使用图片,而Ngui需要使用图集,对图集进行管理和维护

4.UGUI有锚点,可以自动适配屏幕,NGUI没有暂未发现此功能

5.UGUI中Btn需要有sprite,button,而NGUI只需要一个UIButton方法,和一个BoxCollider。

6.NGUI基于C#编写的,会产出比较多的GC,UGUI是基于C++,性能比较好。基于canvas渲染

2.xlua如何热更新、热补丁×

https://cloud.tencent.com/developer/article/2239496

 lua解析器-LuaEnv+dostring(lua语句的require)  --》》lua文件重定向 byte+addloader(File)--》》

C#掉lua:

1.获取全局变量gobal

2.接取函数。若自定义委托[CsharpCallLua],需要先生成Xlua代码,再运行。或者用action。或者用xlua的luafunction

3.lua映射List,Dic,浅拷贝(值拷贝)

4.C#自定义的类可以映射lua的表,浅拷贝(值拷贝)

4.接口用属性可以映射lua的表,[CsharpCallLua],

lua掉C#:

先用C#调用lua,打开入口。

--CS.命名空间.类名

--CS.命名空间.枚举名.枚举成员

用unity自带默认静态方法的用“.”;用类内的方法用“:”。
--Unity的类 比如 GameObject Transform等等 —— CS.UnityEngine.类名

--通过C#中的类 实例化一个对象 lua中没有new 所以我们直接 类名括号就是实例化对象
--默认调用的 相当于就是无参构造
local obj1 = CS.UnityEngine.GameObject()
local obj2 = CS.UnityEngine.GameObject("唐老狮")
--为了方便使用 并且节约性能 定义全局变量存储 C#中的类
--相当于取了一个别名
GameObject = CS.UnityEngine.GameObject
local obj3 = GameObject("唐老狮好爱同学们")

--类中的静态对象 可以直接使用.来调用
local obj4 = GameObject.Find("唐老狮")
--自定义类 使用方法 相同  只是命名空间不同而已
local t = CS.Test()
t:Speak("test说话")
--继承了Mono的类
--继承了Mono的类 是不能直接new 
--GameObject(" ")前面有
local obj5 = GameObject("加脚本测试")
--通过GameObject的 AddComponent添加脚本
--xlua提供了一个重要方法 typeof 可以得到类的Type
--xlua中不支持 无参泛型函数  所以 我们要使用另一个重载
obj5:AddComponent(typeof(CS.LuaCallCSharp))

  lua调C#数组

local array2 = CS.System.Array.CreateInstance(typeof(CS.System.Int32), 10)
print(array2.Length)
print(array2[0])
print(array2[1])
print(array2)

 lua调C#List

--在Lua中创建一个List对象
--老版本
local list2 = CS.System.Collections.Generic["List`1[System.String]"]()
print(list2)
list2:Add("123")
print(list2[0])

--新版本 >v2.1.12
--相当于得到了一个 List<string> 的一个类别名 需要再实例化
local List_String = CS.System.Collections.Generic.List(CS.System.String)
local list3 = List_String()
list3:Add("5555555")
print(list3[0])

 lua调C#字典:get_Item / set_Item

--在Lua中创建一个字典对象
--相当于得到了一个 Dictionary<string, Vector3> 的一个类别名 需要再实例化
local Dic_String_Vector3 = CS.System.Collections.Generic.Dictionary(CS.System.String, CS.UnityEngine.Vector3)
local dic2 = Dic_String_Vector3()
dic2:Add("123", CS.UnityEngine.Vector3.right)
for i,v in pairs(dic2) do
	print(i,v)
end
--在Lua中创建的字典 直接通过键中括号得 得不到 是nil
print(dic2["123"])
print(dic2:TryGetValue("123"))
--如果要通过键获取值 要通过这个固定方法
print(dic2:get_Item("123"))
dic2:set_Item("123", nil)
print(dic2:get_Item("123"))

 --我们不能直接将 lua函数传入到开启协程中!!!!!
--如果要把lua函数当做协程函数传入
--必须 先调用 xlua.util中的cs_generator(lua函数)

 lua 热补丁

--我们必须做4个非常重要的操作
--1.加特性
--2.加宏 第一次开发热补丁需要加HOTFIX_ENABLE
--3.生成代码
--4.hotfix 注入  --注入时可能报错 提示你要引入Tools

--热补丁的缺点:只要我们修改了热补丁类的代码,我们就需要重新执行第4步!!!
--需要重新点击 注入

--lua当中 热补丁代码固定写法
--xlua.hotfix(类, "函数名", lua函数)
--xlua.hotfix(类, {函数名 = 函数, 函数名 = 函数....})

--lua当中 热补丁代码固定写法
--xlua.hotfix(类, "函数名", lua函数)

--成员函数 第一个参数 self
xlua.hotfix(CS.HotfixMain, "Add", function(self, a, b)
	return a + b
end)
--静态函数 不用传第一个参数
xlua.hotfix(CS.HotfixMain, "Speak", function(a)
	print(a)
end)

--xlua.hotfix(类, {函数名 = 函数, 函数名 = 函数....})
xlua.hotfix(CS.HotfixMain, {
	Update = function(self)
		print(os.time())
	end,
	Add = function(self, a, b )
		return a + b
	end,
	Speak = function(a)
		print(a)
	end
})

xlua.hotfix(CS.HotfixTest, {
	--构造函数 热补丁固定写法[".ctor"]!!!!
	--他们和别的函数不同 不是替换 是先调用原逻辑 再调用lua逻辑
	[".ctor"] = function()
		print("Lua热补丁构造函数")
	end,

	--析构函数固定写法Finalize
	Finalize = function()
		
	end
})

热补丁协程【util,cs_generator】

--xlua.hotfix(类, {函数名 = 函数, 函数名 = 函数....})
--要在lua中配合C#协程函数  那么必使用它
util = require("xlua.util")
xlua.hotfix(CS.HotfixMain, {
	TestCoroutine = function(self)
		--返回一个正儿八经的 xlua处理过的lua协程函数
		return util.cs_generator(function()
			while true do
				coroutine.yield(CS.UnityEngine.WaitForSeconds(1))
				print("Lua打补丁后的协程函数")
			end
		end)
	end
})

热补丁事件 

--此时CS.HotfixMain已经有event UnityAction myEvent
xlua.hotfix(CS.HotfixMain, {
	--add_事件名 代表着时间加操作
	--remove_事件名 减操作
	add_myEvent = function(self, del)
		print(del)
		print("添加事件函数")
		--会去尝试使用lua使用C#事件的方法去添加
		--在事件加减的重定向lua函数中
		--千万不要把传入的委托往事件里存
		--否则会死循环
		--会把传入的 函数 存在lua中!!!!!
		--self:myEvent("+", del)
	end,
	remove_myEvent = function(self, del )
		print(del)
		print("移除事件函数")
	end
})

 

3.谈谈mvc与mvx的思想框架×

开发模式 MVC、MVP、MVVM和MVX框架模式 - 纭卿殇 - 博客园 (cnblogs.com)

MVC、MVP、MVVM架构 - 知乎 (zhihu.com)

三种架构模式——MVC、MVP、MVVM (yii666.com)

MVX框架模式的了解

MVX框架模式:MVC+MVP+MVVM

1、MVC:

Model(模型)+View(视图)+controller(控制器),主要是基于分层的目的,让彼此的职责分开。
View通过Controller来和Model联系,Controller是View和Model的协调者,View和Model不直接联系,基本联系都是单向的。【Model的业务数据改变触发相应事件,通知View业务数据已经发生改变】
用户User通过控制器Controller来操作模板Model从而达到视图View的变化。

2、MVP:

是从MVC模式演变而来的,都是通过Controller/Presenter负责逻辑的处理+Model提供数据+View负责显示。
在MVP中,Presenter完全把View和Model进行了分离,主要的程序逻辑在Presenter里实现。
并且,Presenter和View是没有直接关联的,是通过定义好的接口进行交互,从而使得在变更View的时候可以保持Presenter不变。

3、MVVM:

MVVM是把MVC里的Controller和MVP里的Presenter改成了ViewModel。Model+View+ViewModel

View的变化会自动更新到ViewModel,ViewModel的变化也会自动同步到View上显示。
这种自动同步是因为ViewModel中的属性实现了Observer,当属性变更时都能触发对应的操作。

4.unity如何导航寻路

  1. 创建导航网格

    • 打开Unity编辑器,并确保你的场景已经包含了需要进行导航的地形和障碍物。
    • 选择需要进行导航的地形区域。
    • 在菜单栏中选择"Window" > "AI" > "Navigation"以打开导航窗口。
    • 在导航窗口中,点击"Object"选项卡,并选择场景中的地形对象。
    • 在"Navigation Area"中选择适当的导航区域类型,例如"Walkable"用于地面导航。
    • 点击"Create"按钮,以生成导航网格。
  2. 设置导航代理

    • 在场景中放置一个代表可移动对象(通常是玩家角色)的GameObject。
    • 添加一个NavMesh Agent组件到该GameObject上。
    • 调整NavMesh Agent的半径、高度和其他属性,以确保它与导航网格相匹配。
  3. 寻路目标

    • 在脚本中,你可以通过访问NavMesh Agent组件来设置代理的目标位置。通常,你会使用NavMeshAgent.destination属性来设置目标点。
//agent 组件
agent.SetDestination(target.position);

/*---------------------------------------------------------------补充--------------------------------------------------------*/

1.AB包的上传与下载

AB相较Resource更灵活

1.减少包体大小-》1.压缩资源2.减少初始包大小

2.热更新-》1.资源热更新2.脚本热更新

AssetBuddleBrower【configure,Build,Inspect】

 AB打包出的文件:AB资源文件,AB包文件信息,关键AB包(和目录名一样的包)

 以下用例均来自B站唐老狮 ! ! !B站唐老狮 ! ! !B站唐老狮 ! ! !

 同步加载【同一个AB包不能重复加载会报错(不能加载两次)!!】二步(非主包名)


// Start is called before the first frame update
void Start(){
    //第一步 加载 AB包
    AssetBundle ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "model");
    //第二步 加载AB包中的资源//只是用名字加载 会出现 同名不同类型资源 分不清//建议大家用 泛型加载或者 是 Type指定类型
    GameObject obj = ab.LoadAsset<GameObject>("Cube");
    GameObject obj = ab.LoadAsset("Cube",typeof(GameObject)) as GameObject;

    Instantiate(obj);
}

 异步加载

void Start(){
    StartCoroutine(LoadABRes("head23 1110001"));
}

IEnumerator LoadABRes( string ABName,string resName){
    //第一步 加载AB包
AssetBundleCreateRequest abcr =AssetBundle.LoadFromFileAsync(Application.streamingAssetsPath + "/"+ABName);
    yield return abcr;
    //第二步 加载资源
    AssetBundleRequest abq = abcr.assetBundle.LoadAssetAsync(resName, typeof(Sprite));
    yield return abq;
    
    //image img
    img.sprite=abq.asset as Sprite;
}

 若存在依赖包则也要加载依赖包,四步(主包->固定文件->依赖信息->加载依赖包)

//依赖包的关键知识点一利用主包 获取依赖信息
//加载主包
AssetBundle abMain = AssetBundle.LoadFromFile(Application,streamingAssetsPath + "/"+ "pc");
//加载主包中的固定文件
AssetBundleManifest abManifest = abMain.LoadAsset<AssetBundleManifest>"AssetBundleManifest");
//从固定文件中 得到依赖信息
string[] strs = abManifest.GetAllDependencies("model");
//得到了 依赖包的名字
for (int i =@;i < strs.Length; i++)
AssetBundle,LoadFromFile(Application.streamingAssetsPath +"/" + strs[il);

2.C#调用文件夹、文件流

Unity 常用路径及获取目标路径下的所有文件_unity获取文件夹里所有文件_W-x-W的博客-CSDN博客

3.Scriptable

1.文件配置、2.数据复用、3.更好的处理数据带来的多态

不具备持久化特性

以下用例均来自B站唐老狮 ! ! !

手动创建ScriptableObject

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

[CreateAssetMenu(fileName = "SettingInfo", menuName = "ScriptableObject/ÒôÀÖÒôЧÉèÖÃÐÅÏ¢")]
public class SettingInfo : ScriptableObject
{
    //ÒôÀÖºÍÒôЧµÄ¿ª¹Ø
    public bool musicIsOpen;
    public bool soundIsOpen;

    //ÒôÀÖºÍÒôЧµÄ ´óС
    public float musicValue;
    public float soundValue;
}

 start创建

public SettingInfo info;

void Start(){
     info = ScriptableObject.CreateInstance<SettingInfo>();
}

 多态操作(利用一个基类,继承基类,再父类装子类)

public abstract class ItemEffect : ScriptableObject
{
    public abstract void AddEffect(GameObject obj);
}
[CreateAssetMenu]
public class AddAtkItemEffect : ItemEffect
{
    public int atk;
    public override void AddEffect(GameObject obj)
    {
       //具体加多少攻击力的逻辑
    }
}
[CreateAssetMenu]
public class AddHealthItemEffect : ItemEffect
{
    public int num;
    public override void AddEffect(GameObject obj)
    {
        //通过获取到的对象 让其加血了 加num的值

    }
}
public class ItemObj : MonoBehaviour
{
    public ItemEffect eff;

    private void OnTriggerEnter(Collider other)
    {
        //为和物品产生碰撞的对象加效果
        eff.AddEffect(other.gameObject);
    }
}

4.InputSystem新输入系统

 知识点二 选择开启哪一种输入模式

//选择Inputsystem和 老InputManager的启用情况

// File->Build Setting->Player Setting->0ther->Active Input Handling

//可以同时启用也可以只启用其中之一 每次启用后会重启Unity

//可以处理 任意键 下 拾起 长按 相关的逻辑
//keyBoard. anyKey
//wasPressedThisFrame
//wasReleasedThisFrame
//isPressed
#region 知识点二鼠标各键位 按下抬起 长按//鼠标左键
//mouse.leftButton
//鼠标右键
//mouse.rightButton
//鼠标中键
//mouse.middleButton
//鼠标向前向后键
//mouse.forwardButton;/ /mouse backButton
//按下
if(mouse.leftButton .wasPressedThisFrame){}

 InputAction  ->AddingBiding(输入)  CallbackContext(回调数字)

void Start()
    {
        #region 知识点一 InputAction是什么?
        //顾名思义,InputAction是InputSystem帮助我们封装的输入动作类
        //它的主要作用,是不需要我们通过写代码的形式来处理输入
        //而是直接在Inspector窗口编辑想要处理的输入类型
        //当输入触发时,我们只需要把精力花在输入触发后的逻辑处理上

        //我们在想要用于处理输入动作的类中 
        //申明对应的InputAction类型的成员变量(注意:需要引用命名空间UnityEngine.InputSystem)
        #endregion

        #region 知识点二 InputAction参数相关

        #endregion

        #region 知识点三 InputAction的使用
        //1.启用输入检测
        move.Enable();

        //2.操作监听相关
        //开始操作
        move.started += TestFun;

        //真正触发
        move.performed += (context) =>
        {
            print("触发事件调用");
            //当前状态
            //没有启用 Disabled
            //等待 Waiting
            //开始 Started
            //触发 Performed
            //结束 Canceled
            //context.phase
            print(context.phase);

            //动作行为信息 
            print(context.action.name);

            //控件(设备)信息
            print(context.control.name);

            //获取值
            //context.ReadValue<float>

            //持续时间
            print(context.duration);

            //开始时间
            print(context.startTime);
        };

        //结束操作
        move.canceled += (context) =>
        {
            print("结束事件调用");
        };

        //3.关键参数 CallbackContext
        //当前状态

        //动作行为信息 

        //控件信息

        //获取值

        //持续时间

        //开始时间
}

  private void TestFun(InputAction.CallbackContext context)
    {
        print("开始事件调用");
    }

 输入配置文件,inputaction的集合,以下示例以Lesson9为输入配置文件

Acton1为InputActions的集合,Acton1.Fire为InputAction

//1.创建生成的代码对象
//2.激活输入
 //3.事件监听

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

public class Lesson9 : MonoBehaviour
{
    Lesson9Input input;
    // Start is called before the first frame update
    void Start()
    {
        #region 知识点一 根据配置文件生成C#代码
        //1.选择InputActions文件
        //2.在Inspector窗口设置生成路径,类名,命名空间
        //3.应用后生成代码
        #endregion

        #region 知识点二 使用C#代码进行监听
        //1.创建生成的代码对象
        input = new Lesson9Input();
        //2.激活输入
        input.Enable();
        //3.事件监听
        input.Action1.Fire.performed += (context) =>
        {
            print("开火");
        };

        input.Action2.Space.performed += (context) =>
        {
            print("跳跃");
        };
        #endregion
    }

    // Update is called once per frame
    void Update()
    {
        print(input.Action1.Move.ReadValue<Vector2>());
    }
}

5.替身系统avater

 【Unity】Avatar与AvatarMask系统介绍(TPS.番外篇)_SaFuFuの绝世的博客-CSDN博客

Unity为我们提供了Avatar,即替身系统。这是一种Unity设置的骨骼模式,只要你是人型的角色,就会识别,并根据角色原本的骨骼及命名,创建对应的Avatar。那么只需要大家的骨骼都转换成这种模式,我们就可以实现动画复用了。

6.三大可持续化

7.自动锁敌

8.Where约束

where(泛型类型约束)_hua@happiness的博客-CSDN博客

9.Lua

number string boolean nil function table userdata thread 

10.Xlua和Tolua区别

xLua和tolua都是用于在Unity中进行Lua脚本集成的工具,它们有一些共同点,但也有一些重要区别。以下是它们之间的主要区别:

  1. 开发语言:

    • tolua:tolua是一个使用C++编写的工具,它允许将C#代码绑定到Lua脚本。
    • xLua:xLua是一个使用C#编写的工具,它通过ILRuntime库实现了C#和Lua之间的互操作性。
  2. 运行时性能:

    • tolua:tolua的性能较高,因为它将C#代码生成为C++代码,然后与Lua脚本集成。这使得tolua在运行时具有较好的性能。
    • xLua:xLua的性能相对较低,因为它使用ILRuntime库来在C#和Lua之间进行交互。ILRuntime的运行时性能比tolua的C++绑定稍差。
  3. 平台支持:

    • tolua:tolua支持多个平台,包括Windows、iOS、Android等。
    • xLua:xLua也支持多个平台,但与tolua相比,它在某些平台上的性能可能会有所下降。

11.开闭包??

开包(Unpacking)是将数据结构解构为单独的变量【元组】

而闭包(Closure)是带有捕获外部作用域变量的函数,保留状态和引用。【嵌套函数】

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值