GameFrameWork框架(Unity3D)使用笔记(五)游戏主流程ProcedureMain

目录

前言:

一、切换到ProcedureMain流程

二、场景布置

三、生成主角

1、EntityLogic

2、EntityData

(1)Id

(2)TypeId

(3)Position

(4)Rotation

 成功!


前言:

        前面已经完成了UI界面切换等功能,但是真正的游戏内容主体部分还没有开始,下面就来实现游戏的主流程。

        本篇的内容又用到了GF的一个新模块:Entity。这一次我们主要实现的就是在场景中生成主角。


一、切换到ProcedureMain流程

        首先先给“开始”按钮按钮增加一下切换到ProcedureMain的功能。

        之前已经写过了,按下开始按钮会设置m_StartGametrue:

//MenuForm.cs

//ProcedureMenu.cs然后就是在菜单流程中的OnUpdate里面检测m_StartGame,如果是true则利用转换流程ProcedureChangeScene到主流程:

  protected override void OnUpdate(IFsm<GameFramework.Procedure.IProcedureManager> procedureOwner, float elapseSeconds, float realElapseSeconds)
        {
            base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds);

            if (m_StartGame)
            {
                //先设置要转换的下一个场景的ID:
                procedureOwner.SetData<VarInt32>("NextSceneId",GameEntry.Config.GetInt("Scene.Main"));
                //然后进行场景切换流程
                ChangeState<ProcedureChangeScene>(procedureOwner);
            }
        }

 然后在场景切换流程里面,写好了切换到主流程的逻辑:

#ProcedureChangeScene.cs
protected override void OnUpdate(IFsm<GameFramework.Procedure.IProcedureManager> procedureOwner, float elapseSeconds, float realElapseSeconds)
        {
            base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds);

            if (!m_IsChangeSceneComplete)   
            {
                return;  //还没完成场景切换
            }
            if (m_ChangeToMenu)
            {
                ChangeState<ProcedureMenu>(procedureOwner);   //菜单
            }else
            {
                ChangeState<ProcedureMain>(procedureOwner);   //游戏运行主流程
            }
        }

别忘了加到Build Settings里面: 

然后测试试试:

 

从结果可以看出,场景确实切换了,但是UI并没有关闭, 经过调查是上次自己在测试的时候注释掉了“在菜单流程结束的时候关闭菜单UI界面”的逻辑,恢复后就解决了:

ProcedureMenu.cs
protected override void OnLeave(IFsm<GameFramework.Procedure.IProcedureManager> procedureOwner, bool isShutdown)
        {
            base.OnLeave(procedureOwner, isShutdown);

            GameEntry.Event.Unsubscribe(OpenUIFormSuccessEventArgs.EventId, OnOpenUIFormSuccess);

            Log.Info("退出菜单流程!");
            
            if (m_MenuForm != null)
            {
                m_MenuForm.Close(isShutdown);
                m_MenuForm = null;
            }
            
        }

 然后就切换到主游戏场景和流程了。


二、场景布置

        目前场景是空的,因为我们这个是2D游戏所以先在场景上把简单的关卡墙体啥的摆好:

        (我直接拿以前的项目的东西来做了,包括一些2D游戏需要的插件的安装这里省去了,因为文字内容重点在GF框架)。

        ​​​​​​​

这个关卡的任务很简单,就是主角从左下角出生,然后走到门进去,通关!


三、生成主角

        之前主角的控制脚本都是直接一把梭的,既然这里用了框架最好就是用状态机复刻一下。并且把逻辑和数据分离(参考StarForce),不过这里为了节约时间就直接用之前做好的预制体,主要是用来展示一下实体的生成方式。        

然后预制体:

1、EntityLogic

然后,和UI的使用方法很类似(需要实现一个继承UIFormLogic的类),我们需要为这个实体写一个继承EntityLogic 类的脚本:

 然后,我们在游戏流程里加上生成实体的语句。

在此之前,因为所有实体都有一个唯一的ID用来标识,为了方便统一地生成这个ID,我们先写一个扩展方法:

                                                            ​​​

然后我们就可以在主流程里写生成实体的语句:

然后运行,点击开始游戏按钮:

发现人物并没有出现。

其实如果经验够丰富的话,你应该能猜到为啥没有出现。实际上人物实体已经生成了,但是我们并没有指定它的初始位置,所以我们打开框架下的Entity模块可以找到生成的Character:

而它的位置是预制体中设置的位置:

那怎么才能在生成这个玩家Entity的时候指定它所生成的位置呢?

首先,一个比较完整的实体,应该包含逻辑(Logic)数据(Data)吧。我们刚才写了这个Player的逻辑(Logic)(继承了EntityLogic),是不是还缺少数据部分(EntityData)?

那我们下面就来实现数据部分。

2、EntityData

EntityLogic使用方法不同的是,框架里面没有可以让我们来继承的EntityData类,所以我们需要自己实现自己的EntityData类。

先实现一个抽象类EntityData,让其它实体去继承它。所以我们需要考虑所有实体都共有的属性。这里我们参照StarForce的EntityData的实现:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
[Serializable]
public abstract class EntityData
{
    [SerializeField]
    private int m_Id = 0;

    [SerializeField]
    private int m_TypeId = 0;

    [SerializeField]
    private Vector3 m_Position = Vector3.zero;

    [SerializeField]
    private Quaternion m_Rotation = Quaternion.identity;

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

    /// <summary>
    /// 实体编号。
    /// </summary>
    public int Id
    {
        get
        {
            return m_Id;
        }
    }

    /// <summary>
    /// 实体类型编号。
    /// </summary>
    public int TypeId
    {
        get
        {
            return m_TypeId;
        }
    }

    /// <summary>
    /// 实体位置。
    /// </summary>
    public Vector3 Position
    {
        get
        {
            return m_Position;
        }
        set
        {
            m_Position = value;
        }
    }

    /// <summary>
    /// 实体朝向。
    /// </summary>
    public Quaternion Rotation
    {
        get
        {
            return m_Rotation;
        }
        set
        {
            m_Rotation = value;
        }
    }
}

 当然目录结构也要管理好:

下面来解释一下这个EntityData的内容:

里面有四个属性,都是在Unity中生成的实体所共有的特点。

(1)Id

        实体编号

        用于唯一标识一个实体,所有实体的ID互不相同。

(2)TypeId

        实体类型编号

        首先这里需要明白一件事情,根据框架作者的项目可以看出,游戏中所有对象(如怪物、玩家)都是从配置文件(数据表)中加载配置,然后再创建出来。

        然后,举个例子,策划设计了史莱姆怪物,然后为它们写数据表Shilaimu.txt。然后史莱姆怪物分好多种类,比如雷史莱姆,草史莱姆,风史莱姆,火史莱姆,岩史莱姆, 冰史莱姆,然后在配置表中,大概是这样:

        每一种怪物都有个ID,具体体现就是数据表的第一列的ID,这个就是TypeId

        那TypeIdId的区别是啥呢?Id是在整个实体框架里面唯一标识一个实体,而TypeId则标识了一个种类。比如场景中可能有两个雷史莱姆,他们的TypeId是一样的,因为它们种类一样,但是他们的Id不一样,因为他们是不同的个体。

(3)Position

        位置

        每个在场景里的物体一定都有个位置,是一个三维向量。

(4)Rotation

        旋转

        每个在场景里的物体一定都有个朝向,是一个四元数。     

        抽象基类写好了下面就来写PlayerPlayerData类。:

       

目前我们的目的只是把玩家显示到特定位置,所以这里暂且先继承一下写好的抽象基类就行。

然后改一改显示实体的代码,在playerData里面设置了初始位置。

然后运行看看:

(红色报错可以无视,是角色动画部分复制过来的时候没设置好)

 emm角色还是不在场景里面。原因是这样的:

虽然我们设置了Player这个实体的数据部分PlayerDataPosition信息,但是这个信息只是被实体的playerData携带了,并没有”应用“出来,而要把生成的实体“移动”到我们规定的位置,还需要在实体的EntityLogic部分对其进行实现(Player.cs)。

所以我们在Player类里面的Init()函数里面加上改变实体位置到playerData.Position处的逻辑:

CachedTransform是父类里的一个属性,缓存的是实体的Transform。这里的意思就是在实体初始化的时候设置好TransformPosition为之前在playerData里设置的Position(ShowEntity函数传递进去的playerData最终会到达这里函数的userData参数)

然后运行看看结果:

 成功!


这次成功将实体加载到了场景里面,鉴于篇幅本篇先到这里为止。接下来要做的工作将围绕继续完成这一关的流程进行。


                                                                 

        

        

猜你喜欢

转载自blog.csdn.net/HowToPause/article/details/127489522