1.设计初心
在玩家接取任务/交付任务时,界面弹出的UI ,需要与玩家互动,点击“接取”“完成”。等等字样
【改动前】
频繁的手动点击会中断玩家跑图的流畅性,也降低了任务寻路系统的实际体验。
于是现在变成类似FakeObj + 对话框的模式去显示任务的提示字。不再显示任务的接取 / 完成 状态。
自动接取任务。
【改动后】
2.逻辑梳理
在任务完成时通过ObjNpc.ShowDialg(); 显示弹窗
现在需要改动这里面的内容。
bool ShowMission()
{
if (true == SetFindNpcMisCompleted())
{
return true;
}
// 弹出已完成任务
if (true == PopCommitNpcDialog())
{
return true;
}
// 弹出可接任务
if (true == PopAcceptNpcDialog())
{
return true;
}
return false;
}
拿已经完成的任务来说,需要改动 PopCommitNpcDialog()
现在在任务MisBase配置表中加了新的一列,决定是否启用对话框代替任务弹板。当PopType 字段为1 表示启用
第一步读取字典,判断显示方式
Tab_MisBase misBase = TableManager.GetMisBaseByID(nMisID);
//弹窗
if (misBase.PopType == 0)
{
//...
}
else
{
//...
StoryPlayerController.PlayMisDialogue(1, MissionManager.MisCommit,nMisID);
}
在PlayMisDialogue方法中我们需要一个任务ID 和 当前任务状态来标识。
public static bool PlayMisDialogue(int misState,OnFinshMisTips finshMisTips = null,int param = -1)
{
m_curMisState = misState;
Init(finshMisTips, param);
UIManager.CloseUI(UIInfo.OnlineReminderWindow);
return UIManager.ShowUI(UIInfo.StoryPlayer);
}
我设置了三个参数,第一个是任务号标识当前任务,第二个需要接受一个对话框显示结束后的逻辑,可能需要更新任务状态,同步服务器(已完成)等等。
static void Init(OnFinshMisTips finshMisTips = null, int param = -1)
{
m_finshMisTips = finshMisTips;
misID = param;
m_StoryTab = null;
isMisTips = true;
if (m_Instance != null)
{
m_Instance.isPlayFinish = false;
m_Instance.m_BaseOffset.SetActive(true);
}
}
Init作为初始化对话框的前序操作。主要是存储数据到文件里。
接下来是异步的关闭提示窗口打开剧情对话框。
播放剧情逻辑
private void PlayMissionTips(int misState)
{
if (!isMisTips)
{
OnPlayOver();
return;
}
Tab_MisDictionary misDictionary = TableManager.GetMisDictionaryByID(misID);
string dialogue = string.Empty;
if (misState == 0)
{
dialogue = misDictionary.GetAceptDescbyIndex(0);
}
else
{
dialogue = misDictionary.GetComitDescbyIndex(0);
}
int curNpcId = misState == 0 ? misDictionary.AceptNpcDataID : misDictionary.ComitNpcDataID;
//生成对话框头像
InitCharUI(curNpcId);
m_strAniDialog = dialogue;
ShowAniDialogEnd();
m_fStoryTimer = 5f;
isMisTips = false;
//播放结束后处理
m_curMisState = -1;
}
初始化对话框人物头像和角色信息
private void InitCharUI(int nSpeakerID)
{
string strSpeakerName = string.Empty;
int nDataID = nSpeakerID;
Obj_Char objChar = null;
if (GlobeVar.INVALID_ID == nDataID)
{
// 主角
nDataID = ObjManager.MainPlayer.RoleBaseID;
strSpeakerName = ObjManager.MainPlayer.RoleName;
m_SpeakerName.text = strSpeakerName;
if (m_NpcFakeObj)
{
m_NpcFakeObj.gameObject.SetActive(false);
}
if (m_ChildFakeObj)
{
m_ChildFakeObj.gameObject.SetActive(false);
}
// 已经有缓存了
if (m_PlayerFakeObj != null)
{
m_PlayerFakeObj.gameObject.SetActive(true);
m_PlayerFakeObj.ResetFakeObjTransform();
RefixFakeObjCamera(m_PlayerFakeObj);
return;
}
// 创建模型
GameManager.CurActiveScene.ShowFakeCamera(true);
m_PlayerFakeObj = m_UIObjTexture.ShowMainPlayerFakeObj(FAKEOBJTYPE.HALF, RefixFakeObjCamera);
return;
}
else if (-2 == nDataID)
{
// 孩子
if (m_PlayerFakeObj)
{
m_PlayerFakeObj.gameObject.SetActive(false);
}
if (m_NpcFakeObj)
{
m_NpcFakeObj.gameObject.SetActive(false);
}
// 已经有缓存了
if (m_ChildFakeObj != null)
{
m_ChildFakeObj.gameObject.SetActive(true);
m_ChildFakeObj.ResetFakeObjTransform();
RefixFakeObjCamera(m_ChildFakeObj);
return;
}
ChildInfo m_CurChild = null;
if (GameManager.PlayerDataPool.PlayerBirthManager.IsHaveSelfChild(true))
{
m_CurChild = GameManager.PlayerDataPool.PlayerBirthManager.m_ChildInfo;
}
if (m_CurChild == null)
{
return;
}
else
{
strSpeakerName = m_CurChild.m_Name;
m_SpeakerName.text = strSpeakerName;
int childType = m_CurChild.GetChildType();
Tab_ChildInfo tabChildInfo = TableManager.GetChildInfoByID(childType);
if (tabChildInfo != null)
{
Tab_CharModel charModel = TableManager.GetCharModelByID(tabChildInfo.BodyIdFakeObj);
if (charModel != null)
{
AvatarLoadInfo info = ChildFashionManager.GetChildAvatarInfo(m_CurChild);
GameManager.CurActiveScene.ShowFakeCamera(true);
//m_ChildFakeObj = ObjManager.CreateFakeObj(charModel.HalfFakeOjbID, info, GlobeVar.INVALID_ID, "Child", null, GlobeVar.FAKEOBJ_LAYER, GlobeVar.INVALID_ID, RefixFakeObjCamera);
m_ChildFakeObj = m_UIObjTexture.ShowFakeObj(charModel.HalfFakeOjbID, info, -1, -1, -1,
RefixFakeObjCamera);
return;
}
}
}
}
else
{
// 普通NPC
Tab_NpcAttr NpcAttr = TableManager.GetNpcAttrByID(nDataID);
if (NpcAttr != null)
{
//冒牌师姐任务特殊处理,npc名字改成玩家名字
if (nDataID == 8345 && GameManager.RunningScene == (int)SCENE_DEFINE.SCENE_MAOPAISHIJIE)
{
strSpeakerName = ObjManager.MainPlayer.RoleName;
}
else
{
strSpeakerName = NpcAttr.Name;
}
m_SpeakerName.text = strSpeakerName;
if (m_PlayerFakeObj)
{
m_PlayerFakeObj.gameObject.SetActive(false);
}
if (m_ChildFakeObj)
{
m_ChildFakeObj.gameObject.SetActive(false);
}
// 已经有缓存了
if (m_NpcFakeObj != null && m_NpcFakeObj.gameObject.name == nDataID.ToString())
{
m_NpcFakeObj.gameObject.SetActive(true);
m_NpcFakeObj.ResetFakeObjTransform();
RefixFakeObjCamera(m_NpcFakeObj);
return;
}
// 创建模型
Tab_RoleBaseAttr RoleBase = TableManager.GetRoleBaseAttrByID(nDataID);
if (RoleBase != null)
{
objChar = ObjManager.FindObjCharInSceneByData(nDataID);
if (objChar != null)
{
Tab_CharModel charModel = TableManager.GetCharModelByID(RoleBase.CharModelID);
if (charModel != null)
{
if (m_NpcFakeObj != null)
{
GameObject.DestroyImmediate(m_NpcFakeObj.gameObject);
}
GameManager.CurActiveScene.ShowFakeCamera(true);
//m_NpcFakeObj = ObjManager.CreateFakeObj(charModel.HalfFakeOjbID, objChar, nDataID.ToString(), null, GlobeVar.FAKEOBJ_LAYER, RefixFakeObjCamera);
m_NpcFakeObj = m_UIObjTexture.ShowFakeObj(charModel.HalfFakeOjbID, objChar, RefixFakeObjCamera);
return;
}
}
else
{
// 找不到
Tab_CharModel charModel = TableManager.GetCharModelByID(RoleBase.CharModelID);
if (charModel != null)
{
if (m_NpcFakeObj != null)
{
GameObject.DestroyImmediate(m_NpcFakeObj.gameObject);
}
int nNpcProfession = GlobeVar.INVALID_ID;
AvatarLoadInfo info = new AvatarLoadInfo();
info.m_BodyId = charModel.Id;
//增加Npc头身分离逻辑
Tab_NpcAttr npcAttr = TableManager.GetNpcAttrByID(nDataID);
if (npcAttr != null && npcAttr.IsHeadBodyLeave == 1)
{
Tab_NpcModelInfo modelinfo = TableManager.GetNpcModelInfoByID(nDataID);
if (modelinfo != null)
{
info.m_FaceId = modelinfo.FaceModelId;
info.m_HairId = modelinfo.HairModelId;
if (modelinfo.WeaponModelId != -1)
{
info.m_WeaponId = modelinfo.WeaponModelId;
nNpcProfession = modelinfo.AvatarProfession;
}
}
}
GameManager.CurActiveScene.ShowFakeCamera(true);
//m_NpcFakeObj = ObjManager.CreateFakeObj(charModel.HalfFakeOjbID, info, -1, nDataID.ToString(), null, GlobeVar.FAKEOBJ_LAYER, GlobeVar.INVALID_ID, RefixFakeObjCamera);
m_NpcFakeObj = m_UIObjTexture.ShowFakeObj(charModel.HalfFakeOjbID, info, nNpcProfession, -1, -1,RefixFakeObjCamera);
return;
}
}
}
}
}
}
他会根据NPCid 分为三类,玩家、场景NPC 和其他Obj 头像(可能是一团火、或者怪物 )
他们读取的配置表不同。
ShowAniDialogEnd()的内容很简单,大概就是把任务提示的内容,保存到本地str,便于后续播放。
播放字幕
在FixUpdate中更新内容
void FixedUpdate()
{
if(isPlayFinish)
return;
Tick_PlayStory();
}
逻辑如下
void Tick_PlayStory()
{
if (false != m_bAniDialog)
{
return;
}
if (StoryTriggerCtl.Instance.IsPlaying())
{
return;
}
if (m_fStoryTimer == GlobeVar.INVALID_ID || m_fStoryTimer <= 0)
{
if (!isMisTips)
{
if (m_nextBlackScreenId > 0 && !m_bAniDialog)
{
StoryTriggerCtl.Instance.ToStartBlackScreen(m_nextBlackScreenId, onPlayFinished: PlayNextCallback);
m_nextBlackScreenId = -1;
}
else
{
PlayNext();
}
}
else
{
PlayMissionTips(m_curMisState);
}
}
else
{
m_fStoryTimer -= Time.fixedDeltaTime;
}
}
当下一个对话内容不为空的时候,PlayNext()
主要是从对应的剧情字典Tab_StoryContent中取相应播放的文案。
void PlayNext()
{
if (m_bAniDialog)
{
// 动态文字展示
ShowAniDialogEnd();
}
else
{
if (m_StoryTab == null)
{
OnPlayOver();
return;
}
m_nStoryIndex++;
Tab_StoryContent mStoryItem = TableManager.GetStoryContentByID(m_StoryTab.StoryID, m_nStoryIndex);
if (null == mStoryItem)
{
OnPlayOver();
return;
}
UpdateStoryUI(mStoryItem);
//更新下一组对话框时间
m_fStoryTimer = mStoryItem.LifeTime;
JudgeSpeaker(mStoryItem);
if (mStoryItem.BlackScreenEffect > 0)
m_nextBlackScreenId = mStoryItem.BlackScreenEffect;
}
}
更新时间间隔在 m_fStoryTimer = mStoryItem.LifeTime;中
在FixUpdate中
m_fStoryTimer -= Time.fixedDeltaTime;计时