[Unity] Gameframework 学习记录 1

24 篇文章 0 订阅

下载框架作者的示例工程 https://github.com/EllanJiang/StarForce
移动到 unity 空项目中
然后再下载 Gameframework https://github.com/EllanJiang/GameFramework 放到项目的 Asset 对应文件夹中
打开项目,一切顺利


笨木头的教程
https://github.com/mutouzdl/gameframework_demo
烟雨的视频教程
https://www.bilibili.com/video/av71419528


一开始看烟雨的视频教程感觉,啊,讲得好快
有些看懂了有些没看懂,果然还是要自己看

先把烟雨最后的总结截一下:

1.分为编辑器资源模式和非编辑器资源模式
2.流程的生命周期
3.同一时间,只能有一个流程在运行
4.离开流程时要注意取消订阅事件,防止逻辑冲突
5.DF中所有资源加载,都是异步的,不会阻塞主线程,依靠回调函数来判断有没有加载完成
6.GameEntry.Resource.InitResources 是一定要有的,它在非编辑器资源模式下起着至关重要的作用,它初始化了所有资源到一个映射表,但是并没有把所有资源都加载到内存


一开始看的时候还不知道为什么识别不到 namespace

在这里插入图片描述

问了别人才知道要设置 adf,说是 Unity 自定程序集 AssemblyDefinitionFile

在这里插入图片描述
在这里插入图片描述

建好了 adf,我重新构建一遍,结果发现错误更多,然后居然是引擎内部的程序集引用的问题

在这里插入图片描述

这就不科学了,LFS 版本的引擎内部应该不会有问题,所以我觉得可能是我项目构建有问题
或许这是因为我需要使用 unity 的 build 来构建

啊……重新构建了一遍还是有这个问题

我这个是把游戏工程文件夹复制粘贴到空项目中,会不会是我这个做法有问题……难受

好吧,这次我是直接用游戏工程文件夹打开,就好了
难受啊,这是哪里的设置有问题


要开始认真看了

我的打算是一遍看一遍复制粘贴项目,照着改,解决改的时候出现的错误,这样的话就能慢慢知道每个东西都是干嘛的了,大概

程序入口为 Hierarchy - Game Framework 的 GameEntry 脚本组件

因此我搞完 Hierarchy - Game Framework 就先复制 GameEntry 脚本过来
然后脚本里面唯二的两个函数被分在 GameEntry.Builtin 脚本和 GameEntry.Custom 脚本中,再复制这两个过来

然后就可以发现 GameEntry.Builtin 是初始化的框架自带的组件,GameEntry.Custom 是初始化自定义的组件,因此直接复制 GameEntry.Custom 过来的话,因为我还没有写自定义组件,所以找不到,会报错。这个时候我还用不到,先删了吧

一开始 Hierarchy - Game Framework - BuiltIn - Procedure 中啥也没有
从头构建游戏流程的话,那就必须要有流程了嘛,所以现在先要复制这个

根据烟雨的分析,我觉得这个流程……好像挺好的呀hhhh
干脆直接拿过来吧
反正之后只需要修改拓展 ProcedureMain 和 ProcedureMenu

Procedure 命名空间都改完了之后出现的错误:

ProcedureMenu 要用到创建好的 UI 类,我暂时没有界面 UI,就把这部分删去了
不过它要求必须重载 UseNativeDialog 是我没想到的

Procedure 中的 GameBaseGameMode 都是基于太空战机这个玩法创建的,那我自然是不能要,之后还是可以借鉴的
比如,他是做了一个游戏模式对应游戏类的字典,啊,这我就不知道为啥了
另外他好像在流程的每一个钩子都要用父类的方法,就比如 OnUpdate 的开头是 base.OnUpdateOnLeave 的结尾是 base.OnLeave
然后有一个转换到 menu 界面的方法

procedureOwner.SetData<VarInt32>("NextSceneId", GameEntry.Config.GetInt("Scene.Menu"));
ChangeState<ProcedureChangeScene>(procedureOwner);

他是要转换到 menu,所以设置这个 menu 的 id,但是实际上状态机里面切换到了 ProcedureChangeScene,那就应该是 ProcedureChangeScene 里面要用到这个 NextSceneId

还有他在 update 中通过计时器来实现延时……哦,这让我知道了原来确实是可以这么做的

ProcedureChangeScene 里面停止声音,恢复正常速度什么的,是把 Main 场景的 OnEnter 的职责分出去了
但是那是因为框架里面已经写好了通用的停止声音,恢复正常速度,就是说,从别的场景里面退出,一样能用,牛啊

然后他根据 id 在 DRScene 中找到对应的场景

怎么判断流程跳转完了呢?他用了一个 m_IsChangeSceneComplete 作为标志
Update 中查看 m_IsChangeSceneComplete 是不是 true,是则 ChangeState
m_IsChangeSceneComplete 在哪里设置为 true 呢,在 OnLoadSceneSuccess,这是一个事件,这个事件在 OnEnter 被订阅,同时 OnEnter 程序块最后执行了 GameEntry.Scene.LoadScene(AssetUtility.GetSceneAsset(drScene.AssetName), Constant.AssetPriority.SceneAsset, this); ,也就是说这是一个异步的加载场景,在进入 ProcedureChangeScene 的时候就立即加载 Scene,加载完毕之后立即跳转流程

Main 流程中的 m_Games.Add(GameMode.Survival, new SurvivalGame()); 就是游戏主逻辑了

但是 SurvivalGame 里面只有一个随机生成陨石的脚本……?为啥捏
啊,又要找了,难受

不过还是先看陨石吧
他用 IDataTable<DRAsteroid> dtAsteroid = GameEntry.DataTable.GetDataTable<DRAsteroid>(); 拿到了一个 datatable,但是它只是用来获得一个数量,制作陨石的 typeid,因为后面有 new AsteroidData(GameEntry.Entity.GenerateSerialId(), 60000 + Utility.Random.GetRandom(dtAsteroid.Count) 这个构造函数是 public AsteroidData(int entityId, int typeId)
好奇怪啊,typeid 是在 60000 的基础上加一个随机数
然后这个 Count 应该是场上所有的陨石数量吧,但是我没有在 AsteroidData 的构造函数里面找到修改这个 Count 的内容。

好吧,之后才看到,这个 Count 是数据表的行数
诶呀,那他这个随机就很怪

AircraftData.cs

public AsteroidData(int entityId, int typeId)
            : base(entityId, typeId, CampType.Neutral)
        {
            IDataTable<DRAsteroid> dtAsteroid = GameEntry.DataTable.GetDataTable<DRAsteroid>();
            DRAsteroid drAsteroid = dtAsteroid.GetDataRow(TypeId);

他输入的这个随机 typeid 只是用在了 base(entityId, typeId, CampType.Neutral)
我还以为是用来选择陨石的类型的

TargetableObjectData.cs

public TargetableObjectData(int entityId, int typeId, CampType camp)
            : base(entityId, typeId)
        {
            m_Camp = camp;
            m_HP = 0;
        }

EntityData.cs

public EntityData(int entityId, int typeId)
        {
            m_Id = entityId;
            m_TypeId = typeId;
        }

一路下来看到这个随机数最终变成了 m_TypeId 实体类型编号
呃……然后就没有用到了
不懂,跳过

在看 AsteroidData 的时候就可以发现,他这个是实体数据,还有一个实体逻辑,两个都属于实体
或许我现在应该看逻辑,嗯
但是我暂时只接触到了陨石,那就先看陨石的逻辑,然后再找找其他物体是在哪里创建的吧

哦,还要看这个逻辑和 data 是怎么对应的

GameEntry.Entity.ShowAsteroid(new AsteroidData(GameEntry.Entity.GenerateSerialId(), 60000 + Utility.Random.GetRandom(dtAsteroid.Count))
                {
                    Position = new Vector3(randomPositionX, 0f, randomPositionZ),
                }) ;

那一看就是因为这个 GameEntry.Entity.ShowAsteroid 了hhh
一看,就跳转到了 EntityExtension.cs
这里面全是类似的函数

EntityExtension.cs

public static void ShowAsteroid(this EntityComponent entityCompoennt, AsteroidData data)
        {
            entityCompoennt.ShowEntity(typeof(Asteroid), "Asteroid", Constant.AssetPriority.AsteroiAsset, data);
        }

EntityExtension.cs

private static void ShowEntity(this EntityComponent entityComponent, Type logicType, string entityGroup, int priority, EntityData data)
        {
            if (data == null)
            {
                Log.Warning("Data is invalid.");
                return;
            }

            IDataTable<DREntity> dtEntity = GameEntry.DataTable.GetDataTable<DREntity>();
            DREntity drEntity = dtEntity.GetDataRow(data.TypeId);
            if (drEntity == null)
            {
                Log.Warning("Can not load entity id '{0}' from data table.", data.TypeId.ToString());
                return;
            }

            entityComponent.ShowEntity(data.Id, logicType, AssetUtility.GetEntityAsset(drEntity.AssetName), entityGroup, priority, data);
        }

EntityComponent.cs

        public void ShowEntity(int entityId, Type entityLogicType, string entityAssetName, string entityGroupName, int priority, object userData)
        {
            if (entityLogicType == null)
            {
                Log.Error("Entity type is invalid.");
                return;
            }

            m_EntityManager.ShowEntity(entityId, entityAssetName, entityGroupName, priority, ShowEntityInfo.Create(entityLogicType, userData));
        }

这个 ShowEntity 的函数,一路找下去,最终找到了一个 IEntityManager 类型的 m_EntityManager,这是一个接口
再跳转的话,就直接找到了接口,还是没有实现
所以应该看 m_EntityManager 的赋值,这个赋值语句的右值里面应该是实现过了

EntityComponent.cs

protected override void Awake()
        {
            base.Awake();

            m_EntityManager = GameFrameworkEntry.GetModule<IEntityManager>();

跳转之后看到,GetModule 这个函数也只是使用输入的泛型找 Module,那么 Module 的初始化在

GameFrameworkEntry.cs

private static readonly GameFrameworkLinkedList<GameFrameworkModule> s_GameFrameworkModules = new GameFrameworkLinkedList<GameFrameworkModule>();

呃……不对,这是空的
哦,对,空的的话就创建一个,惰性的

创建函数里面也就只有这句

GameFrameworkEntry.cs

GameFrameworkModule gameFrameworkModule = (GameFrameworkModule)Activator.CreateInstance(moduleType);

然后

Activator.cs

namespace System
{
    public static class Activator
    {
        public static object CreateInstance(Type type);

难受啊,那我怎么知道根据 type 创建出来的是什么
啊啊

哦,好吧,搜了一下,原来这是 C# 的函数,根据一个 type 类型的对象创建类的实例。是我孤陋寡闻了
那就是信息都放在 moduleType 里面了

GameFrameworkEntry.cs

public static T GetModule<T>() where T : class
        {
            Type typeFromHandle = typeof(T);
            if (!typeFromHandle.IsInterface)
            {
                throw new GameFrameworkException(Utility.Text.Format("You must get module by interface, but '{0}' is not.", typeFromHandle.FullName));
            }

            if (!typeFromHandle.FullName.StartsWith("GameFramework.", StringComparison.Ordinal))
            {
                throw new GameFrameworkException(Utility.Text.Format("You must get a Game Framework module, but '{0}' is not.", typeFromHandle.FullName));
            }

            string text = Utility.Text.Format("{0}.{1}", typeFromHandle.Namespace, typeFromHandle.Name.Substring(1));
            return GetModule(Type.GetType(text) ?? throw new GameFrameworkException(Utility.Text.Format("Can not find Game Framework module type '{0}'.", text))) as T;
        }

moduleType 来自 Type.GetType(text)text 来自 Utility.Text.Format,来自 typeFromHandle.Namespace, typeFromHandle.Name.Substring(1),来自 Type typeFromHandle = typeof(T);
额……难顶,根本不知道这都是个啥
啊……那我就没办法了

我不理解为什么一个接口类的对象没有实现过接口类里面的函数,但是程序还是跑通了,我找不到他到底在哪里实现了
这不科学啊

好吧,还是回来看 ShowAsteroid 吧
组名 优先级 数据
数据是在 SurvivalGame 里面做的
优先级是要在 ScriptsDefinitionConstantConstant.AssetPriority.cs 中定义

好吧,那么这就算看完了
那接下来是该看 SurvivalGame 之外的部分了
他应该是想要通过 Game 类型的不同,控制游戏不同逻辑的部分,但是主角操控等相同的部分他就做成一个,所以没有放在 SurvivalGame 中

后面又看到

ProcedureMain.cs

protected override void OnEnter(ProcedureOwner procedureOwner)
        {
            base.OnEnter(procedureOwner);

            m_GotoMenu = false;
            GameMode gameMode = (GameMode)procedureOwner.GetData<VarByte>("GameMode").Value;
            m_CurrentGame = m_Games[gameMode];
            m_CurrentGame.Initialize();
        }

里面也没有别的
那就只能是 m_CurrentGame.Initialize()
然后初始化里面就是新建 MyAircraft,飞船

那就先把 ProcedureMain 和 ProcedureMenu 里面的内容注释了

然后之后的错误是缺少 AssetUtility
那就复制 AssetsGameMainScriptsUtilityAssetUtility.cs 过来
然后应该是缺少一些定义
那就复制 AssetsGameMainScriptsDefinition 过来
然后缺少 GameBase 和 GameMode
那就复制 AssetsGameMainScriptsGame 过来

这也是一些具体的东西了,比如 GameBase 里面 protected ScrollableBackground SceneBackground,多的都删了

然后是 ProcedurePreLoad 缺 DataTable
那就复制 AssetsGameMainScriptsDataTable 过来

然后是 ProcedureCheckVersion 里面少了 GameEntry 的 BuiltIn
那就还要在 GameEntry.Custom 里面补回

public static BuiltinDataComponent BuiltinData
        {
            get;
            private set;
        }

        private static void InitCustomComponents()
        {
            BuiltinData = UnityGameFramework.Runtime.GameEntry.GetComponent<BuiltinDataComponent>();
        }

然后是 ProcedureLaunch 的 SoundComponent 里面缺 Mute 啥的
那就复制 AssetsGameMainScriptsSound 过来
然后是 AssetsGameMainScriptsUI

然后是 MenuForm 需要知道 m_ProcedureMenu 中启动游戏的函数
那就回 ProcedureNenu 补上

public void StartGame()
        {
            m_StartGame = true;
        }

然后是 json 缺 dll
那就复制 AssetsGameMainLibraries 过去
加了之后本来是 rebuild 不了的
但是我之后又把 AI 那里的碰撞伤害给注释掉了,因为那里也是游戏主逻辑相关的
然后就可以 rebuild 了

然后一直有那个 ‘Entity’ does not contain a definition for ‘Available’ 之类的错误,我还以为是我哪里出错了,然后去复制了好多不相关的东西

在这里插入图片描述

后面才看到好像确实是因为框架代码改动了的问题
原来的框架里面的 Entity 是继承了 EntityLogic,所以可以直接通过 Entity 访问到 EntityLogic 里面的 Available 和 CachedTransform
但是新框架里面 Entity 类没有继承 EntityLogic,所以还需要用 Entity.Logic

AssetsGameMainScriptsHPBarHPBarItem.cs
AssetsGameMainScriptsUtilityAIUtility.cs
AssetsGameMainScriptsEntityEntityExtension.cs
AssetsGameMainScriptsSoundSoundExtension.cs

然后 EntityExtension 的 ShowEntity 还少一个 EntityData
那就复制 AssetsGameMainScriptsEntityEntityData 中的 EntityData 过去,Logic 就顺便一起复制吧

麻了,复制过来之后就出了问题,说是我不能加 Logic
呃……我知了
他这定义了两个重名的类
然后我是一个文件一个文件从他那复制过来的
一个 Entity 是框架里面的 Entity.cs
一个是游戏工程里的 Entity.cs
我一开始没有复制工程里的文件
然后编辑器就认为我有一个语句中的 Entity 类的对象是框架里面的 Entity.cs 的
我就以为是框架更新了的问题
然后后面我再复制了游戏工程里的 Entity.cs
编辑器又认为这个语句中的 Entity 类的对象是游戏工程里面的 Entity.cs 的

唉,丢人了丢人了

之后为了方便看,把 AssetsGameMainScriptsEntityEntityLogicEntity.cs 改成了 UEntity.cs
连带着改了很多地方

那么接下来就是没有错误了
估计框架的移植就是这样了?

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值