-
Roslyn|01 在Unity中使用Roslyn TOP NEW
新建一个Roslyn项目 在CodeAnalyzer方案中新建一个分析器脚本,具体代码直接让GPT写,这玩意没必要特意学 比如说我让GPT给我写了一个禁止使用Debug.Log的分析器 using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class D... Read More
-
HybirdCLR原理 TOP NEW
先约定一些名词 hotfix.dll/hotfix.dll.bytes 需要热更新的dll build ab 打ab包 build apk 打安卓apk HotFix层/热更新层 执行热更新代码的地方 AOT层/原生层 无法热更的部分 IL2CPP 你的C#代码会被转成CPP代码,同时,由于你目标系统的不同,编译成的CPP代码是不一样的 AOT/JIT/热更新 泛型共享 List和List这是2个不同的类,这是显而易见的,如果你的项目里使用了这两个List,那么编译后大概会有这样一串代码 public class List { public void Add(A o){...} public void Add(B o){...... Read More
-
Lua通讯原理 TOP NEW
Lua是用C编写的,C#这边操作的最底层,实际上是调用了DLL封装好的Lua API Lua中最关键的概念就是Lua栈,所有交互都通过这个栈进行,这个栈的核心概念只有两点 1是栈底,往上递增 -1是栈顶,往下递减 C调用Lua 取值 /*1.创建一个state*/ lua_State *L = luaL_newstate(); /*2.入栈操作*/ lua_pushstring(L, "I am Bob~"); lua_pushnumber(L,2018); /*3.取值操作*/ /*判断是否可以转为string*/ if( lua_isstring(L,1)){ /*转为string并返回*/ printf("%s",lua_tos... Read More
-
ET|11 登录流程 TOP NEW
创建NetClient纤程并发送消息 登录的入口在类LoginHelper中,只有一个方法 public static class LoginHelper { public static async ETTask Login(Scene root, string account, string password) { root.RemoveComponent<ClientSenderComponent>(); ClientSenderComponent clientSenderComponent = root.AddComponent<ClientSenderComponent>(); ... Read More
-
ET|11 网络通讯 TOP NEW
网络消息的定义 注意,ET 8.0仍然是通过proto定义消息的,但是会通过工具转为cs,最后序列化/反序列化的时候使用的是memory pack 协议定义在Config/Proto目录 // ResponseType NetClient2Main_Login message Main2NetClient_Login // IRequest { int32 RpcId = 1; int32 OwnerFiberId = 2; string Account = 3; // 账号 string Password = 4; // 密码 } message NetClient2Main_Login // IResponse { int32 RpcId = 1; ... Read More
-
ET|10 事件的订阅和发布 TOP NEW
首先需要定义一个结构体,结构体的名称就是事件名称,参数就是事件参数 比如 public struct TestEventStruct { public int Test; } 然后是关于事件的订阅 [Event(SceneType.Main)] public class TestEventStruct_Test: AEvent<Scene, TestEventStruct> { protected override async ETTask Run(Scene root, TestEventStruct args) { await ETTask.CompletedTask; } } ... Read More
-
ET|09 Scene层级关系 TOP NEW
-
ET|08 虚拟进程/纤程 TOP NEW
这篇文章开始,学习的是ET 8.0的内容,注意,我这里学习的是Alex大大拆分过后的前后端分离版本的ET Git仓库在这里:ALEXTANGXIAO/GameNetty: GameNetty (github.com) 与原版ET有一定差距,但是不大,主要是前后端拆开了,ET可以作为独立的一部分加入你的工程 框架启动流程 对于客户端而言,启动部分在Init.cs中,这一部分是不可更新的,可以说是工程启动入口 大部分内容都不重要,主要是这一块程序集的获取,作用分为两部分,第一个获取的程序集runtime是会了反射调用启动入口 后面创建一堆程序集,并放入CodeTypes里,是为了做程序集分析,因为ET的一些写法很依赖[Attribute],这里是提前分析好所有类的标签(比如ET... Read More
-
Task与异步编程|02 重新认识Task TOP NEW
经过一定时间的使用,对Task与异步编程有了更加清晰的认知,故翻新一篇新的文章作为记录 异步的前世今生 这一部分内容基本翻译自 How Async/Await Really Works in C# - .NET Blog (microsoft.com) 添加了一小部分个人理解 从一个简单的同步例子开始,一个拷贝字节流的同步方法 // Synchronously copy all data from source to destination. public void CopyStreamToStream(Stream source, Stream destination) { var buffer = new byte[0x1000]; int numRead... Read More
-
Rewired 简单记录 TOP NEW
相关文档 InputManager 数据存储中心,本身不带有复杂逻辑 创建一个Prefab然后把脚本挂上去,运行时生成一个使用,全局唯一 Player 输入控制的中心,你可能会有很多按键布局,比如战斗按键布局,UI按键布局,每种布局可以当做是一个Controller,而一个玩家可以拥有任意数量的Controller,并按照一定的规则启用 如图,一个玩家拥有一个Controller,Controller内包含了很多控制元素,只是最基本的按键信息,比如A 同时,玩家还有一个ControllerMap,也就是一个Controller对应的功能映射,每个映射成为一个Action,比如按键A对应开火 通过这几个组件,就成功把Player、按键A、Fire,三者关联起来 创建... Read More
-
GameAbilitySystem|07 蓝图 TOP NEW
FlowCanvas 蓝图直接使用FlowCanvas即可,但需要自己添加一些节点 OnAbilityStart namespace GameAbilitySystem { [Category("GameAbilitySystem")] public class OnAbilityStart : RouterEventNode<GraphOwner> { private FlowOutput onEnter; private GameAbilitySpec spec; private GameObject owner; protected override void Regist... Read More
-
GameAbilitySystem|06 AbilitySystemComponent TOP NEW
-
GameAbilitySystem|05 GameAbility 技能/能力 TOP NEW
GameAbility 简称GA,GA其实应该叫做 能力 而不是 技能,在GAS中,挨打也是一种GA,所以应该是:“角色具有挨打的能力”,任何行为都可以是一个GA,但还是不建议把基础的移动控制作为一种GA… 能力其实比技能更加抽象,比如我拥有释放火焰的能力,只约定了你在什么时候可以释放火焰,具体怎么释放,动作是什么,效果是什么,这些都不由GA决定,GA只是纯粹的约定,你可以有这个能力,而已 抽象类,是能力的基类,只带有属性,不带有逻辑,我们需要继承这个类,去实现自己的能力 当然,其实这个基类的中的属性,和具体的实现类中的逻辑,是强相关的,也就是说,某个字段对应了什么功能,是约定俗成的(这一点设计上不太好,当然你也可以搞自己的抽象用法。。) namespace GameAbil... Read More
-
GameAbilitySystem|04 GameEffect 游戏效果 TOP NEW
所谓游戏效果(GE),就是技能所附带的功能,技能本身不进行过多的逻辑判断,而是创建一个个的游戏效果,具体的逻辑应该交由游戏效果 GameEffect 标签 GameTagRequireIgnoreContainer 这就是一个容器,包含2个GameTag数组,自身不带逻辑,是交由后面的处理类进行使用的 namespace GameAbilitySystem { [Serializable] public struct GameTagRequireIgnoreContainer { // 一般这里指需要包含的Tag public GameTag[] requireTags; // 一般这里指禁止包含的Tag... Read More
-
GameAbilitySystem|03 Magnitude 规格 TOP NEW
规格,这个翻译很怪,但我也想不到什么更好的词语 生命周期=100*力量,这里的100,就是规格 规格代表一种尺度,是对数值的一种缩放,或者说数值公式 BaseMagnitude 规格的抽象基类,只有两个接口(GameEffectSpec,简称GES,游戏效果执行器,后文细说) 初始化 计算数值 namespace GameAbilitySystem { /// <summary> /// 规格,根据需求返回一个特定的数值 /// </summary> public abstract class BaseMagnitude : ScriptableObject { public... Read More
-
GameAbilitySystem|02 GameAttribute 属性 TOP NEW
GameAttribute 属性标签,仅仅是一个标签,没有具体的数值 namespace GameAbilitySystem { /// <summary> /// 不包含任何数据,仅仅是一个标记 /// </summary> [CreateAssetMenu(menuName = "GameAbilitySystem/Attribute")] public class GameAttribute : ScriptableObject { public string name; /// 用于计算当前这个一级属性的具体数值, 这个类本身只是一种标签,具体的数值,肯定是要结合具... Read More
-
GameAbilitySystem|01 Tag 游戏标签 TOP NEW
-
GameAbilitySystem|00 总览 TOP NEW
-
UGUI源码分析|06 Graphic TOP NEW
-
UGUI源码分析|05 LayoutRebuilder TOP NEW
主要负责刷新UI的位置和大小 核心方法就是MarkLayoutForRebuild MarkLayoutForRebuild public static void MarkLayoutForRebuild(RectTransform rect) { if (rect == null || rect.gameObject == null) return; // 这一部分是找到最上层带有ILayoutGroup的父节点 var comps = ListPool<Component>.Get(); bool validLayoutGroup = true; RectTransform layoutRoot = rec... Read More
-
UGUI源码分析|04 CanvasUpdateRegistry TOP NEW
管理Canvas重建相关的事情,是一个单例类 在构造方法中,向Canvas的事件里注册了一个方法 protected CanvasUpdateRegistry() { Canvas.willRenderCanvases += PerformUpdate; } Cavnas会在渲染前,调用这个方法 有两个核心属性:m_LayoutRebuildQueue、m_GraphicRebuildQueue 两个队列,前者用于管理需要重构的布局,后者用于管理需要重构的图形 UGUI组件一般会根据需要,在OnEnalbe或者其他时候向CanvasUpdateRegistry注册这两个属性 而方法PerformUpdate则是对着两个队列内的属性进行重构,大概顺序如下: 剔除... Read More
-
UGUI源码分析|03 UIBehaviour TOP NEW
UIBehaviour是UGUI的基础类,是对MonoBehaviour的再封装,常规Mono的生命周期它都有,还有一些专门用于UGUI的方法 OnRectTransformDimensionsChange 当RectTransform变化时候调用,Anchors、Pivot、Width、Height变化时调用,Transform、Rotation、Scale变化时不调用 OnBeforeTransformParentChanged 当父物体变化之前调用 OnTransformParentChanged 当父物体变化之后调用 OnCanvasHierarchyChanged 当Canvas状态变化时调用,比如禁用Canvas组件 OnCanvasGroupChan... Read More
-
UGUI源码分析|02 RayCast TOP NEW
射线检测的本质就是,在屏幕上的点击位置发出一条射线,按照一定规则,筛选被射线命中的物体,最后在排序拿到第一个物体,然后进行各种操作 大致涉及一下类: Ray: 射线类 RaycastResult: 投射结果 RaycasterManager: 投射器管理器 BaseRaycaster: 射线投射基类 GraphicRaycaster : BaseRaycaster: 图形投射器 PhysicsRaycaster : BaseRaycaster: 针对3D物体的投射器, 需要对象上同时存在Camera组件 Physics2DRaycaster : PhysicsRaycaster: 针对2D物体的投射器, 需要对象上同时存在Camera组件 Ray ... Read More
-
UGUI源码分析|01 EventSystem TOP NEW
用于处理点击、键盘输入、触摸等事件,所以实际上仅仅只是InputEventSystem EventSystem继承自UIBehaviour,而后者又继承自MonoBehaviour,所以EventSystem本质还是一个Mono 代码一开头,定义了两个输入模块 private List<BaseInputModule> m_SystemInputModules = new List<BaseInputModule>(); private BaseInputModule m_CurrentInputModule; 很明显,一个是所有的输入模块,一个是当前使用中的输入模块 在Update中会调用一个Tick方法,去更新所有的模块 private voi... Read More
-
ET|07 ET框架技术点研究 TOP NEW
协程锁 协程锁机制 - ET社区 (et-framework.cn) 举个客户端的例子,资源加载ab包,ab包假如使用异步加载,那可能出现一个协程正在加载ab包,还没加载完,另外协程又开始加载这个ab包,那就出问题了。我们显然希望第二个协程要等待第一个协程加载完成,再往下执行,这样可以判断这个包加载过了就不需要再加载了。 核心API CoroutineLockComponent.Instance.Wait(CoroutineLockType,Key,OuterTime); CoroutineLockType 协程锁类型,是一个Enum public static class CoroutineLockType { public con... Read More
-
ET|06 Protobuf与网络通讯 TOP NEW
Protobuf 序列化/反序列化 序列化 :Class实例对象 »> byte、xml、json等 反序列化 :byte、xml、json等 »> Class实例对象 序列化的好处有很多,比如可以存到硬盘,比如可以压缩数据等等 Protobuf 类似于json的一种序列化格式,可以做到数据压缩更小,反序列化速度更快 是谷歌开源的一种数据存储格式,需要我们定义好Proto描述文件,然后通过谷歌提供的对应的代码生成工具,生成对于语言的代码 工程内的Proto描述文件,位于./Proto中 OuterMessage 客户端与服务端通信 InnerMessage 服务器之间进行通讯 MongoMessage 服务器之间进行通讯... Read More
-
ET|05 事件与异步编程 TOP NEW
事件 定义事件数据 以服务端为例,在EventType中定义自己事件所需要的数据类型 public struct TestEvent { public string name; public int age; } 定义事件行为 public class TestEvent: AEvent<EventType.TestEvent> { protected override void Run(EventType.TestEvent a) { Log.Error($"{a.name} {a.age}"); } } 派发 派发就很简单了 Game.EventSystem.Publish(new Ev... Read More
-
ET|04 ET接入LuBan TOP NEW
LuBan作为工具直接放在ET目录 ET/LuBan 客户端 GenClient.bat配置如下: set GEN_CLIENT=dotnet .\Tools\Luban.ClientServer\Luban.ClientServer.dll set CLIENT_CODE =..\Unity\Codes\Model\Generate\LuBan\Code #代码生成路径 set CLIENT_DATA =..\Unity\Codes\Model\Generate\LuBan\Data #json生成路径 %GEN_CLIENT% -j cfg --^ -d Defines\__root__.xml ^ --input_data_dir Datas ^ --... Read More
-
ET|03 ECS组件式编程 TOP NEW
区别于Unity的ECS以及Entitas,更像是Unity Mono的编程模式 以一台电脑为例,进行ET组件式编程的模板教程,电脑作为实体,而他的组件就是各种配件,比如机箱,显示器等 Entity & Component Entity 实体只是一个概念上的定义,指的是一个独立物体,对ECS来说,Entity是组件的集合 Component 组件是数据的集合,不产生行为 创建电脑实体 继承Entity表示这是一个实体类,继承IAwake是框架要求,用于初始化 public class Computer: Entity, IAwake { } 创建一个机箱组件 [ComponentOf]很明显了,标记这个类作为Computer类的组件... Read More
-
ET|02 前后端通讯 TOP NEW
登录流程 UILoginComponentSystem 更多框架细节先不讨论,只要知道,当打开登录UI后,会执行UILoginComponentAwakeSystem中的Awake方法 [ObjectSystem] public class UILoginComponentAwakeSystem : AwakeSystem<UILoginComponent> { public override void Awake(UILoginComponent self) { ReferenceCollector rc = self.GetParent<UI>().GameObject.GetComponent<ReferenceColl... Read More
-
ET|01 环境搭建 TOP NEW
-
ECGameplay|04 效果和状态 TOP NEW
配置文件 这一章往后很多效果依赖于数据,所以需要配表 直接使用Luban,不多讲述了(略) 效果 效果依附于能力,比如普攻,最基础的普攻自带是带有伤害效果,但普攻还可以附带比如减速效果 所以说,效果是基于能力的再一次抽象 万物皆效果,效果可以看做是一种BUFF,比如普攻会造成伤害,但我们的普攻不直接进行伤害结算,而是给目标添加一个效果BUFF,具体逻辑由BUFF去触发 EffectComponent 和我们的ActionPointComponent一样,本身没有太多复杂的逻辑,主要是为了集中处理Entity上的Effect namespace ECGameplay { public class EffectComponent : Component {... Read More
-
ECGameplay|03 行动机制 TOP NEW
-
ECGameplay|02 数值系统 TOP NEW
概览 以攻击力为例,在大多数游戏中,无非是装备加成,以及BUFF(状态)加成两种 第一层攻击 = 初始攻击 第二层攻击 = (第一层攻击+装备固定加成)*(1+装备百分比加成) 第三层攻击 = (第二层攻击+BUFF固定加成)*(1+BUFF百分比加成) 数值抽象 不难抽象出一个数值类 FloatNumeric namespace ECGameplay { public class FloatNumeric : Entity { // 最终数值 public float Value { get; private set; } // 基础数值 public float base... Read More
-
ECGameplay|01 组件式编程 TOP NEW
概览 EC,即Entity - Component,是一种代码组织方式 Unity也是基于组件式编程的,一个GameObject上可以挂载多个相同或者不同的组件 我们的Entity,可以类比于一个空的GameObject 同样的地方在于,Entity本身没有任何行为和数据,他的特性由他挂载的所有脚本决定 不同的地方在于,Entity只是一个基类,提供一些通用方法,而我们需要实现自己所需要的所有具体Entity,Entity是数据和行为的结合体 Component代表了某一种行为,而Entity则是多种行为的结合体 Entity 基础架构 主要就是记录父子节点,以及维持自身的组件映射表 namespace ECGameplay { public abstrac... Read More
-
Timeline|03 Animancer TOP NEW
一个基于Playable的扩展插件(当然你也可以基于Playable自己写一套规则) Basic 最简单的播放一个动画,Play有多个参数,主要看前两个 目标 AnimationClip / AnimancerState / ITransition FadeTime 过渡时间/秒 FadeMode 过度模式 public sealed class PlayAnimationOnEnable : MonoBehaviour { [SerializeField] private AnimancerComponent _Animancer; [SerializeField] private AnimationClip _Animat... Read More
-
Timeline|02 Playable原理分析 TOP NEW
有啥好处就不空谈了,直接上干货 架构 Playable有很多类型,但是总体上,由两部分组成 Playable PlayableOutput 从名字就看得出来,他两是对应的,一个是输入,一个是输出 Playable 作为输入节点,一共支持如下类型 PlayableOutput 作为输出,类型就少很多了,因为从上面的输入来看,我们也只有三种类型的输出 PlayableGraph 输入输出管理器,可以看做是一个管理类,管理我们所有的输入(Playable)和输出(PlayableOutput) 如果使用可视化插件,我们可以发现,一个简单的Graph如下(一个输入,一个输出) 当然,Graph通常是不止两个节点的 简单使用 一个模型,挂在... Read More
-
配置|Luban接入记录 TOP NEW
-
GameFramework|14 UI源码分析 TOP NEW
主要流程解析 UIManager.OpenUIForm 是打开UI的入口 界面资源很明显就是对应UI的Prefab,界面组应该是一种逻辑上的结构,后面三个参数比较好理解 **m_Serial **应该是界面编号,随着界面的打开会不断的增加 **m_InstancePool **很明显是一个对象池,首先尝试从对象池中获取UI资源(GF中,ObjectPool是具体资源对象池,ReferencePool是逻辑类对象池) 假设我们从来没有加载过这个UI资源,那么自然对象池里也没有对象,那么会进入 If 逻辑 m_UIFormsBeingLoaded 是一个字典,仅仅用于记录当前那些资源界面正在被加载中,加载结束后会被从字典中删除 随后调用资源管理器加载资源,资源管理器部分的... Read More
-
面试复习 TOP NEW
C# 重载与重写的区别 重载是在类内,发生在编译阶段,重写是发生在父子类之间,运行时override,修改了函数表的函数地址 深浅拷贝的区别 浅拷贝是引用,直接指向同一个内存地址,拷贝变量的修改会直接影响到原变量 深拷贝则是值,会构造一个新的内存区域,并把值完全复制过来 值类型和引用类型 / 堆和栈 值类型:int float之类的基础类型加上枚举和结构体 引用类型:数组,类对象,string 区别在于值类型的大小是确定的,被分配在了栈上,而引用类型可以指向任何对象,大小未知,被分配到了堆上 栈是由操作系统分配的空间,存储短期数据块,超出作用域自动释放 堆则需要人工申请和释放,用于存储长期数据,哪怕超出作用域甚至引用丢失了也不会主动释放,C#中依赖于于GC回收 ... Read More
-
面试记录 TOP NEW
勇仕 一面 2022-9-28 Struct和Class的区别?什么时候用Struct什么时候用类? 只需要访问信息但不需要修改数据时可以用Struct,常见如UI通讯;CLR建议小于16字节时使用Struct Struct可以继承吗,为什么?可以实现接口吗? 底层是密封的,不可以继承;可以实现接口 如果把一个很大的Struct作为参数有什么问题?会装箱吗? Struct作为参数如果不加ref,会导致一次很大的内存拷贝;不会装箱 合批的原理是什么,包括场景和UI? 场景合批的原理是CPU可以把状态相同(材质)的顶点一次性送入CPU中,本质上是计算M矩阵时可以直接用CPU... Read More
-
Asset详解 TOP NEW
Asset资源映射 什么是Asset Assets目录 创建一个新项目时,会有一些默认目录 Assets 实际资源目录 Library 存放Unity处理完毕的资源,大部分的资源导入到Assets目录之后,还需要通过Unity转化成Unity认可的文件,转化后的文件会存储在这个目录 Packages 2018以后新增的目录,用于管理Unity分离的packages组件 ProjectSettings 存放Unity的各种项目设定 AssetBundles 等同于ZIP或者RAR等压缩文件,但是只针对于Unity的Asset,拥有平台差异性,且不包含代码 Unity Asset 也就是Unity能够识别的文件,一种是... Read More
-
Timeline|01 基础介绍 TOP NEW
四大脚本 Data - PlayerBehaviour 每一个Clip所包含的数据,属于Clip Clip - PlayerAsset 轨道上的每一个片段 Mixer - PlayerBehaviour 可以理解为一个总的Clip,用于混合所有的子Clip Track - TrackAsset 轨道,用于存放Clip 自定义轨道 以物品移动为例创建一个简单的自定义轨道 创建Data 这代表我们Clip里所需要用到的数据 [System.Serializable] public class TransformTweenBehaviour : PlayableBehaviour { public Transform endLocati... Read More
-
Unity3D网络游戏实战|05 整体设计 TOP NEW
服务端架构 总体框架 处理玩家数据 存储玩家数据 一个简单的服务器架构图如下 模块划分 网络层:连接客户端,解析协议 消息层:属于游戏逻辑层,不同的消息肯定会有不同的逻辑处理 数据层:访问数据库进行数据存储 JSON的编码和解码 服务端有两个地方会涉及编码和解码,一是处理消息时,和客户端一样需要编码和解码,二是在玩家存储数据时,还需要编码和解码。设计一个名为PlayerData 的数据,里面包含需要存储的信息,如金币等级等,不会直接操作数据库,而是访问这个数据类,在必要的时候直接把这个数据类存储到数据库中 添加协议文件 将客户端的部分脚本直接移植到服务端 区别在于服务度不能使用客户端的Json编码器,因为那是Unity提供的... Read More
-
Unity3D网络游戏实战|04 接口封装 TOP NEW
-
Unity3D网络游戏实战|03 TCP TOP NEW
基础控制 首先制作一个简单的角色控制器,极简版就行,这部分内容与学习内容无关 简单封装一下这个类就叫Player Player是由当前玩家主动控制的角色 public class Player : MonoBehaviour { public NavMeshAgent agent; void Update() { if (Input.GetMouseButtonDown(0)) { var ray = Camera.main.ScreenPointToRay(Input.mousePosition); Debug.DrawRay(ray.origin, ray.dire... Read More
-
动态骨骼|01 Magica Soft TOP NEW
也可以用Dynamic Bone,不过这个插件更新,而且已经用Jobs优化过了,DB的话得自己优化 官方文档 安装 安装Burst,用于将C#编译成native code 安装Collections,几个必须的高效容器 安装Jobs 2021以下的Unity可以在Package Mangaer里找到 2021以上的需要通过Git Url添加 com.unity.jobs 安装Entites(可选,之后可以加入MAGICACLOTH_ECS宏,开启插件ECS) 导入插件 简单说明 MagicaPhysicsManager 相当于中心管理器,每个场景都需要,或者DontDes... Read More
-
Rigidbody记录 TOP NEW
ForceMode ForceMode为枚举类型,用来控制力的作用方式,假设刚体质量为m=2.0f,力向量为f=(10.0f,0.0f,0.0f),FixedUpdate的执行频率采用系统默认值(0.02s) 动量定理:$ft=mv$ ForceMode.Force 默认方式,使用刚体的质量计算,以每帧间隔时间为单位计算动量,即 \(f*0.02=2*v \\ v=f*0.01=(0.1,0,0)m/s\) ForceMode.Acceleration 忽略刚体的实际质量而采用默认值1.0f,即 \(f*0.02=1*v \\ v=f*0.02=(0.2,0,0)m/s\) ForceMode.Impul... Read More
-
热更原理 TOP NEW
C# 之 CLI C# 的编译过程 代码通过C#编译器变异成通用中间件语言(CIL) CIL通过虚拟机(运行时),编译成成对应系统的机器码,并执行 很明显,C#是一种解释型语言,与直接编译成机器码的C/C++这类编译型语言不同,C#的底层平台是运行时,而C/C++的底层平台直接是操作系统 CLI的定义 CLI指的是一种标准,就比如USB设计规范,只要满足这种规范的USB设备,那都可以进行连接,而CLI的规范则包括 虚拟执行系统(VES,运行时) 公共中间语言(CIL) 公共类型系统(CTS) 公共语言规范(CLS) 元数据(metadata) 框架(Framework) CLI并非只是为C#的服务的,倒不如说,C#是满足了CLI规范的一种设... Read More
-
huatuo|02 流程解析 TOP NEW
MonoBehaviour工作流 代码中通过 AddComponent<T>() 或者 AddComponent(Type) 动态挂在的Mono是完美支持的,但是,如果一个Mono脚本被挂在到了Prefab上,并且你想要热更这个脚本,那么就需要一些额外的操作 原理 Unity资源管理,在反序列化资源中的脚本时,要求满足如下条件 必须使用的是AssetBundle打包的资源 脚本所属DLL必须存在于打包生成的ScriptionAssembiles.json中 脚本所依赖的所有DLL已经加载到AppDomain中 如果打包时不做额外操作,那么热更新的DLL肯定是不会出现在json中滴,因此huatuo在脚本中处理了OnPostprocessBuild事件,总的... Read More
-
Huatuo|01 基础使用 TOP NEW
项目配置 huatuo 由两部分构成il2cpp_huatuo 仓库和huatuo 仓库 il2cpp_huatuo 仓库基于 unity 原始 il2cpp 作了少量修改(几百行),使得它可以支持动态注册元数据,进行可以动态加载 dll huatuo 仓库是 huatuo 的核心源代码 注意,il2cpp部分需要和unity版本契合,版本对应关系如图 上面两部分并不用下载,我们到这里,下载示例工程 然后进入项目的HuatuoData目录,这里存放了所有插件相关的东西 其实都是git/svn相关的一些快捷指令,我们不使用这些指令自己手动克隆仓库也是可以的,但偷懒直接用指令吧 首先我们初始化仓库,bat是git命令,sh是svn命令,自己选择合适的就好 运行init... Read More
-
入门精要|06 更复杂的光照 TOP NEW
在此前的章节中,我们存在两个限制: 场景中仅存在一个平行光 不存在阴影 在这一章我们会对这两个效果进行处理 Unity渲染路径 目前主要有两种渲染路径 向前渲染 延迟渲染 我们可以通过Project面板设置,这里设置的是项目统一渲染路径 当然我们也可以为每一个相机单独设置渲染路径 设置完成之后,可以在shader中通过LightMode标签对Pass的渲染路径进行设置 Pass { Tags { "LightMode" = "ForwardBase" } } LightMode支持的渲染路径有: Always 该Pass总会渲染,但不计算光照 ForwardBase 用于向前渲染,该Pass会计算环境光、最重要的平行光、逐顶点/S... Read More
-
Unity3D网络游戏实战|02 基本消息收发 TOP NEW
-
Unity3D网络游戏实战|01基础 TOP NEW
Socket 网络游戏分为客户端与服务端,客户端与服务端之间通过网络协议进行交流,比如TCP、UDP、KCP、HTTP等 此时,每一端都被称为一个Socket 一个Socket包含网络通讯的五种必要信息:协议、本地IP、本地端口、远程IP、远程端口 Socket = 协议 + IP + 端口 TCP通讯流程 先说明一些概念 sequence number: 序列号 acknowledgement number:确认号 SYNchronization:同步标志 ACKnowlegment:确认标志 FINish:终止标志 seq 32位,用来标识从A发送到B的数据包的序号,计算机发送数据时对此进行标记。 ack 32位,客户端和服务器端都可以... Read More
-
Entitas|02 三消游戏 TOP NEW
生成基础游戏页面 游戏面板 用于标记游戏的尺寸,应该是全局唯一的,所以加上[Unique] [Game,Unique] public class BoardComponent : IComponent { public int size; } 关于游戏Item,对应三消游戏, 我们只要应该有以下组件 // 单纯用于标记Item [Game] public class GameItemComponent : IComponent { } // Item关联的视图 [Game] public class ViewComponent : IComponent { public GameObject view; } // Item的坐标,为了 方便起见... Read More
-
Entitas|01 基础介绍 TOP NEW
下载与安装 Release Entitas 1.13.0 · sschmid/Entitas-CSharp · GitHub 下载最新的ECS安装包 解压到Asset目录 随便打开一个脚本,会自动编译Unity项目 然后又如下设置 首先点击Auto Import 然后把Project Path改为自己的项目名称,一般来说就是这个 现在可以点击生成了,会根据Contexts的内容自动生成上下文 默认就两个Game和Input Attribute:一个标签 比如有GameAttribute之后,我们就可以在代码里这么写 这是一个标准组件的写法 [Game] public class PlayerName : ICo... Read More
-
Task与异步编程|01 async/await TOP NEW
引言 异步编程,历史太深,只能非常浅的讲述一些理解 比如以下有趣的代码,Start方法会在await处”被暂停”,但不会阻塞线程,直到我们点击一次Button,才会继续执行 (当然这既不是原生C#,也不是原生Unity,需要我们额外扩展一些方法) public class Test : MonoBehaviour { public Button btn; async void Start() { Debug.Log("Before Click Button..."); await btn; Debug.Log("After Click Button..."); } pri... Read More
-
深入理解C#|04 C#4互操作性提升 TOP NEW
动态类型 简而言之就是可以将代码绑定从编译器转移到代码执行期,可以写类似于脚本语言的方法,感觉没啥用,不记录了 可选形参和命名实参 带默认值的形参和带名字的实参 // 一个必须的形参X,以及两个可选的形参Y、Z public void Method(int x,int y=1,int z=2) { ... } 此时当不输入形参YZ时候,会用默认值输入 默认形参必须放在必须形参之后,且必须符合以下要求之一 编译时常量,值类型,字符串,或者null default表达式 new表达式,只对值类型有效 所以以下调用均合法 Method(1); Method(1,10); Method(1,10,100); 那么如何输入XZ而不输入Y呢? 命名... Read More
-
深入理解C#|03 C#3、LINQ以及相关特性 TOP NEW
隐式类型 静态类型和动态类型 静态类型,表达式检查,方法的调用,一切绑定都在编译期间决定好了。动态类型这把这部分操作放到真正的执行期。(代码先被编译成DLL,等到运行程序时再被真正的执行) C#总体上属于静态类型(C#4引入了动态类型) 显示类型和隐式类型 显式类型即明确的类型,任何变量,字段,方法都需要有一个明确的数据类型 隐式类型 则不必如此 隐式类型的局部变量 以下两种申明方式完全相同 string str = "C#"; var str = "C#"; 使用var关键字,必须满足两个条件 变量在申明时就初始化 用于初始化变量的表达式已经具有某一个类型 只能用于局部变量 隐式类型的数组 var array = new[] {1,2,3}... Read More
-
深入理解C#|02 C#2 TOP NEW
泛型 略 可空值类型 对于一个值类型,他一定是有效的,但他的值不一定是有效的 举例来说,如果需要用户输入一个体重float,当用户没有输入时,float默认就会是0,但很明显,此时的体重是非法的,是应该报错的 解决办法有两个 当没有输入值时,我们用一个默认值代替,系统默认是0,我们可以设置成其他的 额外设置一个bool标志位,当为false时,代表数据非法 很明显这两种解决办法,多少都是有一些问题的,于是产生了可空值类型 Nullable<T>结构体 可空值类型是一种数据结构,早期核心代码如下 public struct Nullable<T> where T : struct { private readonly T v... Read More
-
深入理解C#|01 背景介绍 TOP NEW
概述 类型系统 1.泛型 略 2.可空类型 可空值类型,可空引用类型 // 两者等价 int? a; Nullable<int> a; // 如果a不是null,那么b等于a,否则b等于3 int? b = a ?? 3; 3.隐式局部变量 var a = 1; 4.匿名类型 临时的数据类型,不能用作方法的返回值,因为只是在方法内的临时结构 var book = new { Title = "Test"}; var title = book.Title; 5.元组 元组是值类型变量,可以作为方法的返回值 var book = (title:"Title",price:2f); Console.WriteLine(book.price)... Read More
-
资源管理|03 基本框架 TOP NEW
-
资源管理|02 打包策略分析 TOP NEW
打包策略 制作编辑器工具,统一设置AB包名,与路径管理 根据依赖关系生成不冗余AB包 根据Asset全路径生成依赖关系表 根据依赖关系表加载资源 / 编辑器下直接通过AssetDataBase加载资源 配置文件 打包一共有两种配置 以文件夹为单位,一个文件夹打成一个包 文件夹下的所有文件打成单独的一个包(一般是Prefab) 使用ScriptableObject制作配置表 public class AssetBundleBuildConfig : ScriptableObject { public const string buildConfigName = "AssetBundleBuildConfig"; // ... Read More
-
资源管理|01 常用资源管理方式 TOP NEW
-
UIToolKit|03 技巧 TOP NEW
3D坐标转2D坐标 RuntimePanelUtils.CameraTransformWorldToPanel 随便新建一个血条 public class Test : MonoBehaviour { public UIDocument document; public Transform head; private VisualElement visualElement; private void Start() { visualElement = document.rootVisualElement.Q<VisualElement>("hp"); } private vo... Read More
-
UIToolKit|02 控件 TOP NEW
-
UIToolKit|01 初识 TOP NEW
基础使用 右键Asset -> Create -> UI Toolkit -> UI Document 然后双击打开,可以进入UIBuilder面板 1.创建USS描述文件 2.创建一个UI元素 3.创建一个样式选择器 选中USS描述文件,然后在右侧的选择器里创建 “test”属于修饰名称,没啥实际作用 可以以任意”.x”结尾,”x”才属于是真正的名字,和CSS很像 选中我们新建的样式选择器,再右侧Inspector面板可以调节属性,比如我们把背景设置为红色 可以发现选择器的描述文件发生了变化 4.创建focus和hover选择器 .x是默认选择器,我们可以通过”.x:hover”和”.x:focus”来实现特殊的聚... Read More
-
ILRuntime|05 使用建议 TOP NEW
-
ILRuntime|04 CLR重定向、CLR绑定、值类型绑定 TOP NEW
CLR重定向 啥叫CLR重定向捏,首先,我们热更DLL中的方法,默认情况下!默认情况下是被IL解释器执行的,执行过程我们管不着,交给IL处理就好,但是呢,如果,某些时候,我们希望对这个方法进行一些额外的操作,那么就需要CLR重定向了,类似于重写LR解释器的方法吧,道理是这么个道理,我还没遇到过这样的需求,具体原理我也不懂,做个记录吧。 首先我们打开CLRRedirectionDemo,注释掉一些重定向 unsafe void InitializeILRuntime() { #if DEBUG && (UNITY_EDITOR || UNITY_ANDROID || UNITY_IPHONE) //由于Unity的Profiler接口只允许在主线程使用,为了避免出... Read More
-
迭代器与协程 TOP NEW
什么是迭代器? 在使用迭代器之前,我们如何遍历一个结构? 很简单,用一个for循环就可以遍历了 for(int i = 0;i< nums.Count; i++) { Debug.Log(nums[i]); } 很自然对不对?但是,考虑两个问题: 为什么要从0开始遍历? 为什么每次是+1? 这两个问题,看起来莫名其妙,但是很关键。现在,数组是我们的遍历对象,数组是如何定义的,其实我们是不知道的(下标从0开始只是一种约定) 此时的遍历逻辑,完全是我们外部定义的, 是我们自己写的,而非数组本身提供的,这并不符合设计的初衷 考虑一下,如果要用for遍历一个字典,怎么办?没有办法,因为我们完全不知道内部结构,没法给出遍历逻辑 迭代器模式 迭... Read More
-
坐标详解 TOP NEW
Transform 准备如下脚本 public class ShowPos : MonoBehaviour { public Vector3 world; public Vector3 local; private void Update() { world = transform.position; local = transform.localPosition; } } 挂在GameObject上 运行,可以发现 当Go为场景中的根物体时,世界坐标就等于本地坐标 修改一下脚本,挂到子物体上 public class ShowPos : MonoBehaviour { p... Read More
-
ILRuntime|03 调试、委托、继承 TOP NEW
ILRuntime断点调试 VS有官方插件可以支持,我这边说一下怎么用Rider进行调试 (待补充) 使用委托 如果一个委托在热更中定义,并且被热更中的方法调用,那么我们可以直接调用这个方法,不需要额外处理 如果在主工程中定义了委托,但想要在热更中进行操作,那么需要有适配器以及转换器 如有以下主工程代码 public delegate void TestDelegateMethod(int a); public delegate string TestDelegateFunction(int a); public class DelegateDemo : MonoBehaviour { public static TestDelegateMethod TestM... Read More
-
ILRuntime|02 基础使用 TOP NEW
加载ILRuntime public class HelloWorld : MonoBehaviour { //AppDomain是ILRuntime的入口,最好是在一个单例类中保存,整个游戏全局就一个,这里为了示例方便,每个例子里面都单独做了一个 //大家在正式项目中请全局只创建一个AppDomain AppDomain appdomain; System.IO.MemoryStream fs; System.IO.MemoryStream p; void Start() { StartCoroutine(LoadHotFixAssembly()); } IEnumerator LoadHo... Read More
-
ILRuntime|01 安装和基础设置 TOP NEW
安装 在项目的Packages/manifest.json中,dependencies节点前增加以下代码 "scopedRegistries": [ { "name": "ILRuntime", "url": "https://registry.npmjs.org", "scopes": [ "com.ourpalm" ] } ], 在PackageManager中导入ILRT,并导入官方DEMO 项目设置中同意使用不安全的代码 基础使用 在项目目录下有一个隐藏文件夹,以“~”结尾的文件夹不会被识别到UNITY的资源列表中 打开文件夹,其实这就是我们的热更项目,虽然也放在了Asset之下,但其实和主工程是没... Read More
-
入门精要|05 透明效果 TOP NEW
深度测试 根据深度缓冲,比较两个片元距离摄像机的距离,如果当前片元比起深度缓冲中的值来说,离摄像机更远,那么当前片元不会被写入。 透明度测试 如果片元的透明度不满足条件,则被舍去,不进行任何处理。如果满足条件,那就当做不透明物体进行处理。不需要关闭深度写入。 透明度混合 根据当前片元的透明度,与颜色缓冲中的颜色进行混合,得到新的颜色。需要关闭深度写入。为什么?因为透明物体不会遮挡后面的物体,如果开启了深度缓冲,那么根据深度测试,在该透明物体后方的物体,不会被写入深度缓冲那种。这不是我们所需要的。 渲染顺序 考虑上图,假设A是透明物体,B是不透明物体,此时我们开启了透明度混合,关闭了深度... Read More
-
入门精要|04 基础纹理 TOP NEW
-
入门精要|03 基础光照 TOP NEW
环境光 可以认为就是给物体加一层底色,因为即使在黑暗的情况下,世界上通常也仍然有一些光亮(月亮、远处的光),所以物体几乎永远不会是完全黑暗的。为了模拟这个,我们会使用一个环境光照常量,它永远会给物体一些颜色。 在Windows/Lighting/Settings中的AmbientOcclusion中设置,在Shader中通过UNITY_LIGHTMODEL_AMBIENT得到环境光的颜色和强度 自发光 物体自身的颜色,一般来说需要乘以一个很小的系数,也算是一种底色 $c_{self}=k_{self} * c_{base}$ $k_{self}$:自发光系数,一般来说很小,当然得看具体物体 $c_{base}$:物体自身颜色 漫反射 物体的漫反射取决于物体表演面色、光... Read More
-
入门精要|02 图形学基础02 TOP NEW
输入装配阶段 三维模型本质是成千上万的顶点数据,比如一个正方形就有8个顶点,输入装配阶段则是对于模型顶点的第一次处理,将顶点装配成图元 该阶段不可编程 顶点着色阶段 顶点着色器会遍历传入的每一个顶点进行着色,注意,虽然叫着色器,但并不是字面意义上“计算颜色”,顶点着色阶段还有很多额外的操作,比如最重要的操作就是空间转换 模型空间 所谓模型空间,就是3D建模软件中的空间 世界空间 世界空间,是模型导入U3D之后的空间,模型在模型空间和世界空间中会一样吗?那显然未必,因为我们可以在U3D中对导入的模型进行缩放旋转位移等操作 我们称缩放矩阵为S,旋转矩阵为R,平移矩阵为T,注意顺序只能是:缩放->旋转->平移 即如果有一个模型空间中的点A,想要转换到世界空间B... Read More
-
入门精要|01 图形学基础01 TOP NEW
线性代数基础 单位向量 向量点积 计算投影 因为是B在A上的投影,所以投影向量必然是沿着A方向的 判断方向 点积结果的正负,依赖于夹角的大小,当夹角大于90度时,Cos结果为负,此时可以判断两个向量朝向不同 向量叉积 两个向量叉积的结果,垂直于这两个向量,此时3个向量可以构成一个三维坐标系 判断左右 根据右手定则以及两个向量叉积的结果的正负,很容易的可以判断这两个向量的左右关系 判断某一个点是否在三角形内 利用好上一个规则,如果P在三角形ABC内,则AP在AB的左侧,BP在BC的左侧,CP在CA的左侧,即各项叉积的结果应该都是正或者都是负 否则P不在三角形ABC内 矩阵 矩阵可以表示缩放以及选择,但没法表示平移,于是引入... Read More
-
Unity与Android通信 TOP NEW
前言 目前网上找到的相关文章都是互相转发抄袭,真的毫无阅读价值,如此简单的通信竟然花了我好几天的功夫学习,故做此记录 Android层 准备 环境配置相关的内容就不介绍了 创建一个空项目 下面配置可以随便写,不会用到,但还是规范一点吧! 创建项目,等待Gradle Sync,切记不要使用代理,不要使用代理,也不要开梯子! 右键项目,创建一个new module,选择library 这里的package name要和unity中设置一致,API等级大于11 然后我们把创建工程自带的app module删掉,当然直接删是删不掉的,得在这 同事删除MySDK的所有依赖 删除安卓自带的一些资源文件,只留下main就好 到此为止安卓方面的一个... Read More
-
Job System TOP NEW
前言 本文章学习自这里,这是素材文件的下载链接 多半内容都是文章内容加上自身理解,受限于时间以及自身能力水平,谨慎参考 首先需要3个Unity的官方包,请打开Unity的预览包选项,不然很多预览阶段的包你是无法在包管理器中看到的,具体如图 随后在Package Manager中安装Jobs、Burst以及Mathematics 其中前两个可以直接在包管理器中搜到,而数学库Mathematics需要通过gir url安装,具体如图 在出现的搜索栏中输入com.unity.mathematics@1.1并点击Add就可以添加数学库了 顺带一提,Burst似乎是随着Jobs一起安装的,目前好像不需要额外安装了,具体关系我没有研究,反正保证这三个包都装上了就行 关于Jo... Read More
-
GameFramework|13 数据节点 TOP NEW
用于管理游戏运行时的各种数据信息,以一种树状结构储存 新建如下两个脚本 其中DataNode是数据节点类,Manager就是管理器了 DataNode 字段与属性 namespace SimpleGameFramework.DataNode { public class DataNode { #region Field public static readonly DataNode[] s_EmptyArray = new DataNode[] { }; public static readonly string[] s_PathSplit = new string[] {".", "/",... Read More
-
GameFramework|12 对象池 TOP NEW
-
GameFramework|11 多语言 TOP NEW
多语言也是很常用的一个功能,如果游戏想要支持多种语言,那就不得不设计一个多语言的框架。比如中文的“确定”,英文要叫“OK”,你不可能创建多个语言版本的UI,那会十分繁琐,难不成有二十种语言,你就创建二十种UI?那也太折磨人了… 目前只考虑到文字的多语言,实际上还有其他资源也需要支持本地化,比如众所周知国内的血是绿色的,而国外是红色的,这其实也算是一种本地化…只不过对象不是文字,而是动画或者贴图资源 但目前我这里只学习了文字相关的本地化 其实现很简单,本质是靠CSV表配置,我们给每个Text组件额外加上一个ID,然后根据ID去对应的语言CSV里找对应的语言文字就好 CSV的读取规则 读表是很常见的需求,但我们还是必须制定好表格的规则,首先,我们的KEY单独为一张表,这张表里指定... Read More
-
GameFramework|10 流程Procedure TOP NEW
所谓流程,本质是对于状态机的再封装。一个游戏在运行是,不可能全程处于一个状态,游戏运行的不同状态,我称其为流程,比如刚打开游戏的时候,可能处在登录页面,或者说黑屏动画的时候在加载资源,这就是游戏运行的两个不同状态,这就是流程。 ProcedureBase 流程基类,本质就是一个状态 /// <summary> /// 流程基类 /// </summary> public class ProcedureBase : FsmState<ProcedureManager> { public override void OnEnter(Fsm<ProcedureManager> fsm) { base... Read More
-
GameFramework|09 Fsm有限状态机 TOP NEW
状态机很好理解,它包含了很多的状态,最简单的例子就是Unity自带的Animator Controller,他本身就是一个状态机,里面有很多的动画状态,不同的状态代表不同的动画,然后我们给不同状态之间加上条件,描述状态的转移。 这个模块本身是很好理解的,但目前我也不是很清楚具体会应用在什么地方,参照UGF的描述,一些用于状态切换的游戏逻辑,是可以写在这个模块的。当然,我们后面还有一个流程管理模块Procedure,这个模块就是基于状态机的再封装,也算是一个应用。 咱们新建如下脚本 其中 IFsm是状态机接口 Fsm是状态机 FsmState是状态,状态机管理很多状态 FsmManager是状态机管理类,我们可以有很多状态机 Fsm 定义状态机接口 /// <... Read More
-
GameFramework|08 事件池 TOP NEW
上一节我们写了一个简单的引用池,而单一的引用池是没有啥作用的,事件池就是引用池的作用之一 我们当然可以把事件定义在模块的内部,比如把一个打开UI的事件定义在UI模块内,但是随着我们模块的不断增多,如果所有事件都定义在自己的模块内,在后期是很难维护的,我们都不知道一共定义了哪些事件…因此,一个全局的事件管理中心,是很有必要的 事件池 事件池是实际上保存事件的地方,可以添加或者取消订阅事件。 在此之前,我希望我们的委托遵循.NET规范,即应该是如下格式 public delegate void TestEventHandler(Object sender, EventArgs e); 其中sender是事件的发送者,而e则是发送的数据 发送者自然是之后会写到的事件管理器,... Read More
-
GameFramework|07 引用池 TOP NEW
引用池,和对象池的概念非常像,但是对象池服务于具体的GameObject对象,而引用池服务于普通的C#类。举个例子,如果打开UI的时候你想传递一些数据,最简单的方法就是 class UIData { //... } main() { UIData data = new UIData(); OpenUI(data); } 这个方法是我在上一节末尾提到的,即定义一个数据类型,然后打开UI的时候直接传进函数里,说实话我感觉这样做…其实也挺好的…很简单直观,感觉也没啥问题… 咳咳,但是!但是你想挑毛病还是有的…比如这个data,每次我们打开一个UI,都需要new一个,然后传参数进去。众所周知,频繁的new肯定是不得劲的!所以引用池避免的就是这个问题… 是... Read More
-
软光栅|04深度缓冲 TOP NEW
从二维开始 假设有这样一张图片,横轴是X轴,纵轴是Y轴 想象一下,假设我们现在是从正面在看这张图,那么如果我们从顶部来看,俯视的看,会是什么样的情况?可能很抽象,尽可能的想象一下,应该是如此,我们看到的应该是图中我画出来的那一条线段。 为什么最开始是一段绿色而不是红色?很明显,因为我们是俯视来看的,那么上面的绿色肯定会遮住下面的红色,后面也同理,是什么决定了我们能看到的颜色?是高度,或者说Y的值,Y更大的颜色会遮挡住Y更小的颜色,这就是深度缓冲。 那么对于这么一张俯视的图来说,我们需要多少个深度缓冲呢?Width x Height吗?其实完全不用,最终我们看到的只是一条线段,我们只需要记录这条线段上,每一个点的深度就好,也就是需要Wdith个深度缓冲。 写段代码可能理... Read More
-
软光栅|03三角形与光栅化 TOP NEW
简单绘制一个三角形 上一节末我们已经能够画一条直线了 void line(int x0, int y0, int x1, int y1, TGAImage& image, TGAColor color) { // ... } 在VS里新建一个geometry.h,然后把代码复制进去,这个头文件里就是一些坐标类型和计算,这个头文件依旧来自最开始介绍的git,你想去自己下载也行~ #ifndef __GEOMETRY_H__ #define __GEOMETRY_H__ #include <cmath> #include <iostream> #include <vector> //////////////////////////... Read More
-
GameFramework|06 UI管理器04 TOP NEW
工具类 上一节的末尾,我们需要创建很多UI来测试,因为需要很多Button事件,所以我们写了很多函数,然后…然后一个一个拖到了Button上…很明显的能够感到,这个过程是真的很痛苦…这才五六个Button而已,而实际上我们可能需要几十个几百个Button事件,如果我们都采用这样直接拖放的方式…那是真的痛苦… 在UI下新建一个静态工具类,就叫UIUtility吧~ /// <summary> /// UI工具类 /// </summary> public static class UIUtility { /// <summary> /// 根据路径获取某个child的某个组件 /// </summary> ... Read More
-
GameFramework|05 UI管理器03 TOP NEW
数据结构设计 我们还不能直接开始设计UI的开闭接口,必须先确定好,应该用什么样的一种格式来储存所有的UI脚本,其实很简单 我们能确定一共会有多少个UI吗?这不一定。 我们需要随机访问UI吗?一般没这个需求。 我们可能会频繁创建和删除UI吗?这倒是有可能的。 注意另一个情况,一般来说,我们游戏的UI打开是递进的,关闭UI同样如此,比如说,我们先打开商城,然后购买时弹出确认提示,此时我们应该先关闭确认提示,然后才能关闭商城,是不是很像先进后出的数据结构? 那么很明显了,其实没啥特殊的,我们用栈来维护所有UI就行了,但不能只有一个栈,因为3种UI基本上是独立的,所以在这里我设计了3个栈,分别维护三种界面UI,同时,还需要一个字典类型,记录了所有已经被载入的UI。 public ... Read More
-
GameFramework|04 UI管理器02 TOP NEW
UI的注册 好了,在上一节结尾,我们已经能打开一个UI了,那么我们的管理器完成了吗? 是的,完成了……个锤子! 最直观的问题是,我们现在打开的UI是固定的,废话,因为Resources加载的路径都被写死了…… public void OpenUI() { /// 路径是死的!! GameObject go = Resources.Load<GameObject>("UI/TestUI"); var prefab = GameObject.Instantiate(go); prefab.transform.SetParent(normalRoot,false); } 所以第一个问题:如何保存我们UI Prefab的路径? 最简单的... Read More
-
GameFramework|03 UI管理器01 TOP NEW
理论 首先,为什么需要一个UI框架?抛开所有的想法, 假设我们用最最最基础的做法来做一个UI,我们会怎么操作?很简单,作为入门的一个小白,我可能会在场景里拖一个canvas,然后设置他的active为false,并在需要的时候激活canvas,那样就能显示和隐藏 这没有问题,毕竟我的第一个UI界面就是这么写的。。。 咳咳,但是!但是我们的游戏不可能只有一个界面啊,比如商店、背包、人物属性等等,UI的数量绝对是难以预计的多。。难不成给每一个UI都拖一个canvas?当然理论上这是可行的。。就像你可以把整个游戏的逻辑都写在一个Update()里,理论上,应该也是可行的。。 好了,说认真点,框架还是为了提高维护性和拓展性,当UI越来越多的时候,如果我们不进行统一的管理,那会造成很多不... Read More
-
软光栅|02 画一条直线 TOP NEW
用离散的思维考虑问题! 在现实世界我们用笔画一条直线,那它看起来就是光滑的、连续的一条直线,但在计算机世界,如果我们在屏幕上也画一条直线…好吧那看起来也很光滑连续…但你要知道其实这是因为屏幕分辨率比较高,如果我们不断放大图片…那就会像这样: 图一:10%缩放 图二:200%缩放 好吧这其实是用Excel画的,上面那张图看起来已经有一点棱角了,但你们就当他是光滑的吧!能明白意思就好!计算机世界的图像都是以像素为单位绘制的,放大看肯定是会像图二那样,而不是现实世界的连续直线 也因此,像素是没有类似(0.5,0.5)的坐标的,像素是最小单位,必然是整数,只能是(0,0)、(1,1)、(1,2),注意,像素和数组一样,是从0开始计算的 但我们数学意义上的坐标是... Read More
-
软光栅|01 前言 TOP NEW
说在前面! 本系列并非是教程,只是个人学习感想,关于图形学,个人是感兴趣的,但也只是感兴趣而已,并没有像大佬们那样学习的很深 本系列主要学习自这里,链接里的文章写的很好,但是很多知识谈的太粗略,对于懂的大佬自然懂,不懂的萌新看着会比较吃力 本系列不会讲述太细节的图形学基础知识,但能提到尽量都会提,纯萌新的话还是建议先看看Games101的图形学入门课程 本系列完全只考虑图形学相关的知识,例如矩阵计算库或者win32等知识都不会涉及,这些会直接调用现成的库 准备工作 用VS新建一个空的项目,我取名叫做SoftwareRender 把这个仓库下载下来,里面两个文件是作为我们绘制的库进行使用,把他们丢... Read More
-
GameFramework|02 一切从管理器出发 TOP NEW
万事开头难! 想了半天,应该从哪儿开始呢,算了还是别开始了… 哈哈开玩笑的~让我们先新建一个Unity项目,并给咱们这个框架取一个响当当的名字:SimpleGameFramework,简称SGF(好吧我承认名字很随便,你随意~) 项目结构: SimpleGameFramework 好吧这只是一个毫无看点的空项目…那么我们该从哪儿开始学习呢? 在上一篇的结尾我曾经提出过一个想法,即:把系统分为很多不同的模块,每个模块只关心自己的事情。那么很自然的会有一个问题,如果我们的项目有很多模块,那我们要怎么统一管理这些模块呢?当然我们也可以不统一管理…随便管理就好,但后果就是当模块数量越来越多的时候,我们就开始难以控制他们,不知道什么模块在什么时候被什么调用… 可能大家还... Read More
-
GameFramework|01 前言 TOP NEW
什么是框架? 在所有事情开始之前,我想问一个问题:框架是什么? 我本科是学JAVA的,搞JAVA的同学都清楚,JAVA系的框架那叫一个成熟那叫一个多,虽然我没啥兴趣学,但好歹也在学校的课程安排下使用过很多,但我从来没想过这个问题,什么叫框架? 单例模式是框架吗?MVC是框架吗?库是框架吗?System.IO是框架吗? 到现在我也没法给出一个很严谨的定义,但按照我的理解,框架是一种约束性的代码组织方式,它即约束了你的项目风格(包括代码格式和资源存放地址等),但同时也极大的方便了项目的管理与维护。 举个栗子! 我们项目里有很多图片文件,我们可以把他们都放在一个叫image的文件夹下,当然,我们也可以把他们放在一个叫text的文件夹下,但我相信能看懂这两个英文单词的人都会选择前者的做法,... Read More