因为这部分已经做完了,回头整理文档顺便复盘,所以可能不是很详细,先给了整体代码,后面再慢慢分析
GameFramework框架
写这个例子前最好看下框架的教程
我们用开发模版,很多内容已经写好了,首先下载
搜索New Prefab,然后都删掉
这是整体的目录结构,框架整体是流程procedure来控制的,游戏的入口在GameFramework-Builtin-Procedure的设置面板里,Available Procedures是所有的游戏流程,Entrance Procedure就是第一个执行的流程,即入口。
流程 (Procedure) – 是贯穿游戏运行时整个生命周期的有限状态机。通过流程,将不同的游戏状态进行解耦将是一个非常好的习惯。对于网络游戏,你可能需要如检查资源流程、更新资源流程、检查服务器列表流程、选择服务器流程、登录服务器流程、创建角色流程等流程,而对于单机游戏,你可能需要在游戏选择菜单流程和游戏实际玩法流程之间做切换。如果想增加流程,只要派生自 ProcedureBase 类并实现自己的流程类即可使用。
打开入口文件ProcedureLaunch,重命名namespace名为MaliGame,vs编辑器会自动替换所有文件,修改命名空间后,还要修改下面几个地方
加载登陆界面
首先确定分辨率,添加640*1136
然后新建一个scene,改名为Load修改摄像机模式为solid Color,并添加一个Image,因为Image需要Canvas为容器,所以会自动创建一个Canvas。
修改Canvas为LoadCanvas,Image为LoadBkg,并创建按钮,改名为registBtn,删除自带的Button,添加CommonButton
复制一个改名为loginBtn,并按图创建其他内容,在Procedure目录下创建游戏目录MaliGame,新建脚本取名ProcedureLoad,创建LoadScene脚本,并添加到LoadCanvas上
因为这部分是已经做完了,回头整理文档,所以介绍比较快,跟着来就好了。按图添加其他的text和image,并拖到对应的位置,脚本完整内容如下
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace MaliGame
{
public class LoadScene : MonoBehaviour
{
[HideInInspector]
public bool IsStartGame = false; //登陆完成进入游戏
public CommonButton registBtn;
public CommonButton loginBtn;
public Image progressFg;
public Text loadLb;
public GameObject shipEff;
private float proNum = 0.0f;
public float animSpeed = 10; //动画播放速度 默认1秒播放10帧图片
private float animTimeInterval = 0; //帧与帧间隔的时间
public Image animRenderer;//动画载体的渲染器
public Sprite[] SpriteArray; //序列帧数组
private int frameIndex = 0; //帧索引
private int animLength = 0; //多少帧
private float animTimer = 0; //动画时间计时器
void Start()
{
Debug.Log("LoadScene Start");
startLoad();
animTimeInterval = 1 / animSpeed;//得到每一帧的时间间隔
animLength = SpriteArray.Length; //得到帧数
}
void Update()
{
//假进度,正常是加载的时候更新进度
if (proNum < 1)
{
proNum += 0.05f;
setProgress(proNum);
}
animTimer += Time.deltaTime;
if (animTimer > animTimeInterval)
{
animTimer -= animTimeInterval;//当计时器减去一个周期的时间
frameIndex++;//当帧数自增(播放下一帧)
frameIndex %= animLength;//判断是否到达最大帧数,到了就从新开始 这里是循环播放的
animRenderer.sprite = SpriteArray[frameIndex]; //替换图片实现动画
}
}
//更新进度条
private void setProgress(float pNum)
{
Debug.Log("LoadScene setProgress " + pNum + "%");
proNum = pNum;
loadLb.text = (int)(pNum * 100) + "%";
progressFg.fillAmount = pNum;
shipEff.transform.SetLocalPositionX(-300 + 600 * pNum);
if (proNum >= 1)
{
loadLb.text = "加载完成";
progressFg.fillAmount = 1;
shipEff.transform.SetLocalPositionX(300);
loadOver();
}
}
//开始加载资源
private void startLoad()
{
Debug.Log("LoadScene startLoad");
setProgress(proNum);
//var registBtn = (CommonButton)this.GetComponent("registBtn");
registBtn.gameObject.SetActive(false);
//var loginBtn = (CommonButton)this.GetComponent("registBtn");
loginBtn.gameObject.SetActive(false);
}
private void loadOver()
{
Debug.Log("LoadScene loadOver");
//var registBtn = (CommonButton)this.GetComponent("registBtn");
registBtn.gameObject.SetActive(true);
//var loginBtn = (CommonButton)this.GetComponent("registBtn");
loginBtn.gameObject.SetActive(true);
}
public void OnRegistBtnClk()
{
Debug.Log("LoadScene OnRegistBtnClk");
GameEntry.UI.OpenUIForm(UIFormId.LoginForm, this);
}
public void OnLoginBtnClk()
{
Debug.Log("LoadScene OnLoginBtnClk");
IsStartGame = true;
}
}
}
ProcedureLoad代码如下
using GameFramework.Event;
using GameFramework.Procedure;
using UnityEngine;
using UnityGameFramework.Runtime;
using ProcedureOwener = GameFramework.Fsm.IFsm<GameFramework.Procedure.IProcedureManager>;
namespace MaliGame
{
public class ProcedureLoad : ProcedureBase
{
public LoadScene m_LoadScene;
//private LoginForm m_LoginForm = null;
//进入加载流程的时候
protected override void OnEnter(ProcedureOwener procedureOwener)
{
Debug.Log("ProcedureLoad OnEnter");
base.OnEnter(procedureOwener);
m_LoadScene = GameObject.Find("LoadCanvas").GetComponent<LoadScene>();
}
protected override void OnUpdate(ProcedureOwener ProcedureOwener, float elapseSeconds, float realElapseSeconds)
{
Debug.Log("ProcedureLoad OnUpdate " + m_LoadScene.IsStartGame);
base.OnUpdate(ProcedureOwener, elapseSeconds, realElapseSeconds);
if (m_LoadScene.IsStartGame)
{
ProcedureOwener.SetData<VarInt>(Constant.ProcedureData.NextSceneId, GameEntry.Config.GetInt("Scene.Main"));
ChangeState<ProcedureChangeScene>(ProcedureOwener);
}
}
protected override void OnLeave(ProcedureOwener procedureOwner, bool isShutdown)
{
Debug.Log("ProcedureLoad OnLeave");
base.OnLeave(procedureOwner, isShutdown);
}
}
}
这里在新建一个脚本LoginForm一会说,代码如下
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace Ship
{
public class LoginForm : UGuiForm
{
private LoadScene m_LoadScene = null;
protected override void OnOpen(object userData)
{
Debug.Log("LoginForm OnOpen");
base.OnOpen(userData);
m_LoadScene = (LoadScene)userData;
}
protected override void OnClose(object userData)
{
Debug.Log("LoginForm OnClose");
m_LoadScene = null;
base.OnClose(userData);
}
public void OnStartButtonClick()
{
Debug.Log("LoginForm OnStartButtonClick");
Close();
m_LoadScene.IsStartGame = true;
}
}
}
因为是复盘,所以先把代码都贴出来了,下面来添加素材,然后一步步说。unity里面的图片想要给image设置需要线修改属性,这步比较麻烦,我们来整个脚本,以后导入就直接变了。
在根目录下创建一个Editor文件夹 之后在Editor文件夹下创建一个脚本 TextureChage
using UnityEditor;
using UnityEngine;
public class AssetPostManager : AssetPostprocessor
{
void OnPostprocessTexture(Texture texture)
{
if (assetPath.StartsWith("Assets/Resources/Image"))
{
TextureImporter importer = AssetImporter.GetAtPath(assetPath) as TextureImporter;
if ((importer != null) && (importer.textureType != TextureImporterType.Sprite))
{
importer.textureType = TextureImporterType.Sprite;
importer.SaveAndReimport();
}
}
}
}
先判断一下路径(对于我的项目来说,prefab的图片都是放在这里的,所以我就只修改这个目录下的图片类型)。
接着判断一下,如果不是Sprite就改成Sprite。这个类型判断最好加上,因为可能会影响九宫格的数据。
最后一定要SaveAndReimport()一下,才能看到被修改了。
好了,把素材都拖进来,然后设置给之前创建的对应image,为了查看方便可以选择左上角那个2d模式查看
给按钮设置完图片后,再添加点击事件,OnClick这里点击右下角+号,把LoadCanvas拖到OnClickli ,然后选择对应的事件
这里要注意,需要拖拽的时候哪个canvas,而不是哪个脚本,不然选不了点击事件
load弄好后,在创建一个场景命名Main,和一个预制体LoginForm,先不用放内容,先把流程跑起来。
编写框架流程
这里依次打开我们的场景,添加上场景,另存为按制表符分割的txt文件
这里mac每次编写excel保存导出很是费劲,写了个脚本,修改好excel好后,执行脚本,会自动到处txt供项目使用
#!/usr/bin/python
#encoding:utf-8
# 把当前目录下所有Excel转成以制表符间隔的txt,相当于excel到处txt
# 如果excel存在多个sheet,最后一个生效
import os
import openpyxl
import sys
# 转换编码要不会报错
reload(sys)
sys.setdefaultencoding('utf8')
curPath = os.getcwd() #返回当前文件所在的目录
flie_dir = os.listdir(curPath)
def excelToTxt(file):
#打开Excel文件,这个文件有4个sheet,分别为东,南,西, 北四个区的销售情况
wb = openpyxl.load_workbook('DefaultConfig.xlsx')
#循环来读取每一个sheet中的内容
#写到对应的东,南,西, 北四个txt文件中
for sheet in wb.sheetnames:
#生成一个以sheet名字命名的txt文件
file2 = file.split('.')
file = open(file2[0] + '.txt', 'w')
#打开对应的sheet
ws = wb[sheet]
#循环来读取每一个cell中的内容
for i in range(1, ws.max_row + 1):
for j in range(1, ws.max_column + 1):
#如果cell中的内容为None,那么写到txt中的时候用空来代替
if ws.cell(i, j).value is None:
continue
#如果cell中有内容,将其转换成string写到txt中
else:
content = str(ws.cell(i, j).value)
#如果是第一列那么前面没有制表符分隔
if j == 1:
file.write(content)
#否者内容前面加上一个制表符分隔
else:
file.write("\t" + content)
#每一行写完,换行
file.write("\n")
#关掉txt文件
file.close()
for file in flie_dir:
if file.endswith('.xlsx'):
excelToTxt(file)
这里有个坑,配置里的界面组名称不是unity里的layer分层,而是需要在框架里手动创建的,不然会报错找不到这个default层
都编写完,运行脚本,生成txt,这里写了另一个脚本,区别在于遍历所有的excel
#!/usr/bin/python
#encoding:utf-8
# 把当前目录下所有Excel转成以制表符间隔的txt,相当于excel到处txt
# 如果excel存在多个sheet,最后一个生效
import os
import openpyxl
import sys
# 转换编码要不会报错
reload(sys)
sys.setdefaultencoding('utf8')
curPath = os.getcwd() #返回当前文件所在的目录
flie_dir = os.listdir(curPath)
def excelToTxt(file):
#打开Excel文件,这个文件有4个sheet,分别为东,南,西, 北四个区的销售情况
wb = openpyxl.load_workbook(file)
#循环来读取每一个sheet中的内容
#写到对应的东,南,西, 北四个txt文件中
for sheet in wb.sheetnames:
#生成一个以sheet名字命名的txt文件
file2 = file.split('.')
file = open(file2[0] + '.txt', 'w')
#打开对应的sheet
ws = wb[sheet]
#循环来读取每一个cell中的内容
for i in range(1, ws.max_row + 1):
for j in range(1, ws.max_column + 1):
#如果cell中的内容为None,那么写到txt中的时候用空来代替
if ws.cell(i, j).value is None:
continue
#如果cell中有内容,将其转换成string写到txt中
else:
content = str(ws.cell(i, j).value)
#如果是第一列那么前面没有制表符分隔
if j == 1:
file.write(content)
#否者内容前面加上一个制表符分隔
else:
file.write("\t" + content)
#每一行写完,换行
file.write("\n")
#关掉txt文件
file.close()
for file in flie_dir:
if file.endswith('.xlsx'):
excelToTxt(file)
保存好以后,到GameMain/Scripts/UI/Builtin中修改UIFormId
添加主场景的流程脚本,然后勾选所有流程,并设置初始化流程
接着修改这两个地方,然后应该就可以跑起来了
注意下面这个地方需要设置,UI Groups需要添加一个,InstanceRoot也需要把左侧的拖过来设置下,不然加载form的时候会有问题