降临者

思考并回答以下问题:

  • 所有的加载都调用一个类来处理。这样的好处是:1、可以进行全局管理和优化,方便切换加载方式;2、代码各处都有各自的加载非常混乱,一旦修改就改动的非常多。
  • Vector3.normalized和Vector3.Normalized的区别是什么?
  • i<<=j与i|=j是什么意思?

快速跳转

商业源码

  • 会用到很多设计模式,常见的有单例模式,外观模式与观察者模式,工厂模式等;
  • 用到很多数据结构,例如Queue\,Stack\,还有自定义的数据结构,MyDictionary,BetterList,Heap,LinkedListDictionary等;
  • 能不继承MonoBehaviour的就不继承,然后有很多Manager类,基本是封装一切;
  • 类的层次和引用特别深,通常看懂一个功能需要找好几层,好几个类;
  • 完全看懂需要画UML图和时序图。

配置</span>

1、配置文件格式 \Assets\Resources\Config\Data\Skilldata.xml

1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<SkillConfig>
<skill ID="101001001" isshow="0" weaponType="1" SkillType="0" name="commonattack1" skillDescribe="巨剑普攻1" attack_range="1.5" qishou="1" suo_target="1" no_target="1" icon="zhanshi_attack" initAdd="1" activeLevel="1" attackCoefficient="1" damagePlus="0" lastTick="0" cooldown="0" MPexpend="0" AddStateType="0" nextSkill="101002001" shengji_miaoshu=""/>
<skill ID="101002001" isshow="0" weaponType="1" SkillType="0" name="commonattack2" skillDescribe="巨剑普攻2" attack_range="1.5" suo_target="1" no_target="1" icon="zhanshi_attack" initAdd="1" activeLevel="1" attackCoefficient="1" damagePlus="0" lastTick="0" cooldown="0" MPexpend="0" AddStateType="0" shengji_miaoshu=""/>
<skill ID="509001001" isshow="0" weaponType="0" SkillType="0" name="[FFF563]剑刃风暴[-]" skillDescribe="[FFF555]效果:[-]召唤剑刃风暴击溃敌人,对前方圆形范围内每[00FF00]0.3[-]秒造成[00FF00250.0[-]%+[00FF00]500[-]点伤害,持续[00FF00]3[-]秒" qishou="1" suo_target="0" no_target="1" icon="zhanshi_xuanfengzhan" initAdd="1" activeLevel="1" attackCoefficient="1" damagePlus="4500" lastTick="0" cooldown="30000" MPexpend="0" AddStateType="0" skill_dingwei="范围,雷伤害"/>
</SkillConfig>

FFF555是黄色,00FF00是绿色。一个技能有一个ID。
Assets\Resources\Effect\Effect_Prefab\Role\Skill\zhanshi_xuanfengzhan.prefab特效的位置。使用了FX Maker插件。
Assets\Resources\Picture\Skill\zhanshi_xuanfengzhan.png图片的位置

2、地址常量定义 Assets\Code\UI\Common\Const\GlobalConst.cs 实现文件的相关常量定义

1
2
3
4
5
6
7
8
9
10
// 存储路径的常量
public class PathConst
{
public const string PATH_PREFIX = "Config/Data/"; // 数据配置文件的前缀
public const string SKILL_DATA_PATH = PATH_PREFIX + "Skilldata";
public const string NPC_PATH = PATH_PREFIX + "NPC";

public const string FLOAT_BLOOD_NUM = "UI/Effect/FloatMsg/float_blood_num"; // 飘血的标记
public const string MODEL_CAMERA = "Model/prefab/modelCamera"; //模型相机
}

3、\Assets\Code\UI\Common\ReadLocclData\DataReadBase.cs 从XML读取配置的数据的基类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using UnityEngine;
using System.Collections;

public class DataReadBase
{
public string path; // 配置文件所在路径
protected Hashtable data;

public void init()
{
data = new Hashtable();
}

// XML配置文件的根节点
public virtual string getRootNodeName() { return "";}

// 一段配置文件,key是ID,name是isshow,value是0
public virtual void appendAttribute(int key, string name, string value) {}

/// <summary>
/// 是否已经读取
/// </summary>
/// <param name="key">配置段最前方的id</param>
public virtual bool HasRecord(int key)
{
return data.ContainsKey(key);
}
}

\Assets\Code\Common\ConfigDataManager.cs 就是这个文件读取XML的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
using UnityEngine;
using System.Xml;
using model;
using manager;

public class ConfigDataManager
{
// 单例模式
static private ConfigDataManager sConfigDataManager;

...
private DataReadSkill skill_config;
...

private ConfigDataManager()
{
}

static public ConfigDataManager GetInstance()
{
if (sConfigDataManager == null)
{
sConfigDataManager = new ConfigDataManager();
}
return sConfigDataManager;
}

public void init()
{
...
//DataReadSkill 放置在data中
skill_config = new DataReadSkill();
skill_config.path = PathConst.SKILL_DATA_PATH;
skill_config.init();
read_config(skill_config);

...

//NPC
DataReadNPC readNPC = new DataReadNPC();
readNPC.path = PathConst.NPC_PATH;
read_config(readNPC);
...
}

/// <summary>
/// 从XML文件里读取配置
/// </summary>
/// <param name="dataBase">里氏替换</param>
/// <param name="special"></param>
void read_config(DataReadBase dataBase, int special = 0)
{
Debug.Log("begin read config : " + dataBase.path);

TextAsset ta = null;

if (BundleMemManager.debugVersion)
{
ta = BundleMemManager.Instance.loadResource(dataBase.path) as TextAsset;
}
else
{
AssetBundle bundle = BundleMemManager.Instance.ConfigBundle;
ta = bundle.Load(ToolFunc.TrimPath(dataBase.path)) as TextAsset;
}

if (ta)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(ta.text);

XmlNodeList nodeList = xmlDoc.SelectSingleNode(dataBase.getRootNodeName()).ChildNodes;

for (int k = 0; k < nodeList.Count; k++)
{
XmlElement xe = nodeList.Item(k) as XmlElement;

if (xe == null)
continue;

string key = xe.GetAttribute("ID");

for (int i = 0; i < xe.Attributes.Count; i++)
{
XmlAttribute attr = xe.Attributes[i];
try
{
dataBase.appendAttribute(int.Parse(key), attr.Name, attr.Value);
}
catch (System.Exception ex)
{
int iii = 1;
}

}

if (special == 1) //读取帮助
{

}
else if (special == 2) //读取剧情
{

}

Loger.Log("end read config : " + dataBase.path);
}
}
}

public DataReadSkill getSkillConfig()
{
return skill_config;
}
}

\Assets\Code\UI\Skill\Model\SkillVo.cs 模型类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
using System;
using System.Collections;
using System.Runtime.InteropServices;

namespace model
{
public class SkillVo
{
public SkillVo()
{
Consume = new BetterList<TypeStruct>();
}

/// <summary>
/// xmlID
/// </summary>
public int XmlID { get; set; }
/// <summary>
/// 短ID(去掉level)
/// </summary>
public int SID { get; set; }

public int Level
{
get { return XmlID % 100 == 0 ? 100 : XmlID % 100; }
}

public string Name { get; set; }
public string Icon { get; set; }
public float Attack_Range { get; set; }
public bool Qishou { get; set; }
public int Sou_Target { get; set; }
public int No_Target { get; set; }
public int Cool_Down { get; set; }

public int Mp_Cost { get; set; }

public float Attack_Coefficient { get; set; }

public float Damage_Plus { get; set; }

public string Max_Active { get; set; }

public eSkillType Type { get; set; }

public int Active_Level { get; set; }

public eFighintPropertyCate AddStateType { get; set; }

public float LastTick { get; set; }

public int NextSkillID { get; set; }

public int WeaponType { get; set; }

public string SzDesc { get; set; }

public eGoldType UnLockType { get; set; }
public int UnLockValue { get; set; }

public string SkillLevelDescription { get; set; }

public BetterList<TypeStruct> Consume { get; set; }

public string SkillDescription { get; set; }

public bool IsShow { get; set; }
}
}

\UI\Common\ReadLocalData\DataReadSkill.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
using UnityEngine;
using System.Collections;
using model;
using manager;

// 每个配置段都是一个对象,这个数据实体对象定义了很多对应的字段。
// 数据实体
public class SkillDataItem
{
public int id;
public bool isShow; // 是否在UI上显示此技能
public int nWeaponType;
public eSkillType type;
public string name;
public string szDesc;
public float attack_range;
public bool qishou;
public int suo_target;
public int no_target;
public string max_active; // 最大数据,-1:无此项数据
public string icon;

public uint active_level;
public float attack_coefficient;
public float damage_plus;
public float lastTick;
public int cool_down;
public int mp_cost;
public eFighintPropertyCate addStateType;
public int nextSkill;

public string skillDescription; // 技能描述,UI用
public eGoldType lvlComsumeType1; // 升级花费
public int lvlComsumeValue1;
public eGoldType lvlComsumeType2;
public int lvlComsumeValue2;
public string skillLevelDescription; // 升级描述
public eGoldType reLockComsumeType; // 解锁消耗
public int reLockComsumeValue;
}

public class DataReadSkill : DataReadBase
{
public DataReadSkill() : base()
{

}

public override string getRootNodeName()
{
return "SkillConfig";
}

public override void appendAttribute(int key, string name, string value)
{
SkillVo sv;

if (SkillTalentManager.Instance.SkillHash.ContainsKey(key))
{
sv = SkillTalentManager.Instance.SkillHash[key] as SkillVo;
}
else
{
sv = new SkillVo();
SkillTalentManager.Instance.SkillHash.Add(key, sv);
}

string[] strs;

switch (name)
{
case "ID":
sv.XmlID = int.Parse(value);
sv.SID = sv.XmlID / 1000;
break;
case "name":
sv.Name = value;
break;
case "attack_range":
sv.Attack_Range = float.Parse(value);
break;
case "qishou":
sv.Qishou = int.Parse(value) == 1 ? true : false;
break;
case "suo_target":
sv.Sou_Target = int.Parse(value);
break;
case "no_target":
sv.No_Target = int.Parse(value);
break;
case "cooldown":
sv.Cool_Down = int.Parse(value);
break;
case "icon":
sv.Icon = value;
break;
case "MPexpend":
sv.Mp_Cost = int.Parse(value);
break;
case "attackCoefficient":
sv.Attack_Coefficient = float.Parse(value);
break;
case "damagePlus":
sv.Damage_Plus = float.Parse(value);
break;
case "MaxActive":
sv.Max_Active = value.Trim();
break;
case "SkillType":
sv.Type = (eSkillType)(int.Parse(value));
break;
case "activeLevel":
sv.Active_Level = int.Parse(value);
break;
case "AddStateType":
sv.AddStateType = (eFighintPropertyCate)int.Parse(value);
break;
case "lastTick":
sv.LastTick = float.Parse(value) / 1000.0f;
break;
case "nextSkill":
sv.NextSkillID = int.Parse(value);
break;
case "weaponType":
sv.WeaponType = int.Parse(value);
if (SkillTalentManager.Instance.CareerSkillList.ContainsKey((CHARACTER_CAREER)sv.WeaponType))
{
Hashtable hash = SkillTalentManager.Instance.CareerSkillList[(CHARACTER_CAREER)sv.WeaponType] as Hashtable;
hash[sv.XmlID] = sv;
}
break;

case "skillDescribe":
sv.SzDesc = value;
break;

case "jiesuo_jiage":
string[] s = value.Split(',');
sv.UnLockType = (eGoldType)int.Parse(s[0]);
sv.UnLockValue = int.Parse(s[1]);
break;
case "shengji_miaoshu":
sv.SkillLevelDescription = value;
break;
case "shengji_xiaohao":
strs = value.Split(',');
for (int i = 0; i < strs.Length; i+=2)
{
sv.Consume.Add(new TypeStruct
{
Type = ConsumeType.Gold,
Id = int.Parse(strs[i]),
Value = int.Parse(strs[i+1])
});
}
break;
case "shengji_item":
strs = value.Split(',');
for (int i = 0; i < strs.Length; i += 2)
{
sv.Consume.Add(new TypeStruct
{
Type = ConsumeType.Item,
Id = int.Parse(strs[i]),
Value = int.Parse(strs[i+1])
});
}
break;
case "skill_dingwei":
sv.SkillDescription = value;
break;
case "isshow":
sv.IsShow = int.Parse(value) == 0 ? false : true;
break;

}
}

public SkillDataItem getSkillData(int key)
{
SkillDataItem sdi = new SkillDataItem(); ;
if (!SkillTalentManager.Instance.SkillHash.ContainsKey(key))
{
return sdi;
}

SkillVo sv = SkillTalentManager.Instance.SkillHash[key] as SkillVo;
sdi.id = sv.XmlID;
sdi.name = sv.Name;
sdi.attack_range = sv.Attack_Range;
sdi.qishou = sv.Qishou;
sdi.suo_target = sv.Sou_Target;
sdi.no_target = sv.No_Target;
sdi.cool_down = sv.Cool_Down;
sdi.icon = sv.Icon;
sdi.mp_cost = sv.Mp_Cost;
sdi.attack_coefficient = sv.Attack_Coefficient;
sdi.damage_plus = sv.Damage_Plus;
sdi.max_active = sv.Max_Active;
sdi.type = sv.Type;
sdi.active_level = (uint)sv.Active_Level;
sdi.addStateType = sv.AddStateType;
sdi.lastTick = sv.LastTick;
sdi.nextSkill = sv.NextSkillID;
sdi.nWeaponType = sv.WeaponType;
sdi.szDesc = sv.SzDesc;
sdi.reLockComsumeType = sv.UnLockType;
sdi.reLockComsumeValue = sv.UnLockValue;
sdi.skillLevelDescription = sv.SkillLevelDescription;
sdi.skillDescription = sv.SkillDescription;
sdi.isShow = sv.IsShow;
return sdi;
}

public Hashtable getHashtable()
{
return data;
}
}

Appear

\Code\Character\Appear\BornAppear.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using UnityEngine;
using System.Collections;

public class BornAppear : Appear // 继承Appear
{
float born_time = 4.0f;
Vector3 born_pos; // 出生地点
bool first_update = true;

// 构造函数
public BornAppear()
{
battle_state = BATTLE_STATE.BS_BORN;
}

public override void active()
{
animation_name = "birth";

on_active();
}
}

\Code\Character\Appear\SkillAppear.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
using UnityEngine;
using System.Collections;

public enum SKILL_APPEAR
{
SA_ROLL= 0, // 战士 弓箭手翻滚
SA_MAG_FLASH_AWAY, // 法师翻滚
SA_MAG_FLASH_BACK, // 法师后闪 技能中间一段
SA_WHIRL_WIND,
SA_FIRE_RAIN,
SA_FLASH_AWAY // 哥布林闪现
}

public class SkillAppear : Appear
{
public int skill_id;

public SkillExpressionDataItem m_kSkillInfo;
public SkillDataItem m_kData;

protected float m_fUpdateTime = 0.0f;
public float m_fSkillCD = 0.0f;

// 持续伤害技能第一次的伤害位置判断
public bool m_bDurationalSkillFirstColliderFlag = true;
// 持续伤害技能第一次的碰撞盒判断
public bool m_bDurationalSkillFirstColliderFlag = true;

// 第三方看自己普通攻击是否碰到敌人
public int m_nColliderStopValue = 1;

public bool m_bSkillIntrupted = false;

public bool m_bSkillTriggedJiGuan = false;

public SkillAppear(int skillid)
{
battle_state = Appear.BATTLE_STATE.BS_SKILL;
skill_id = skillid;
}

public virtual void init()
{
if (skill_id != 0)
{
loadConfig();
}
}

public virtual int GetSkillId()
{
return skill_id;
}


}

Effect

\Code\UI\Common\BillBoard.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using UnityEngine;
using System.Collections;

public class BillBoard : MonoBehaviour
{
void Start()
{

}

void LateUpdate()
{
// 第一个启用的摄像机标记为“MainCamera”(只读)。如果场景中没有这样的相机,则返回null。
if (Camera.main)
{
Vector3 v = Camera.main.transform.forward;
// 使用指定的forward和upwards方向创建旋转。
// 返回计算的四元数
// 此函数作用是生成一个四元数表示的三维朝向,然后能够直接把这个朝向赋给游戏对象来变更其朝向,也能够通过线性插值(Quaternion.Slerp 和 Quaternion.Lerp)来实现游戏对象从当前朝向转到这个新生成的朝向上来。
// 使其朝向摄像机
transform.rotation = Quaternion.LookRotation(v);
}
}
}

\Code\UI\Effect\FloatBloodNum.cs 浮血数字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
using UnityEngine;
using System.Collections;
using MVC.entrance.gate;

public enum eHurtType
{
normalHurt = 0, // 正常攻击
doubleHurt, // 暴击
escapeHurt, // 闪避
withstandHurt, // 招架
goblinMoney, // 哥布林金钱
golbinPoison // 哥布林中毒
}

public class FloatBloodNum
{
const int PLUS_NUM = 10; // 加号
const int SUBSTRACT_NUM = 11; // 减号
const int DOUBLE_HIT_NUM = 12; // 暴击
const int ESCAPE_HIT_NUM = 13; // 闪避
const int WITHSTAND_HIT_NUM = 14; //招架
const int NUM_SP = 9;

private UISprite[] _numSps;
private GameObject _escapeSp; // 闪避的Sprite
private GameObject _doubleSp;
private GameObject _withstandSp;
private GameObject _prefab;

// 单例
private static FloatBloodNum _instance;

public static FloatBloodNum Instance
{
get
{
if(_instance == null)
_instance = new FloatBloodNum();
return _instance;
}
}

// 构造函数
private FloatBloodNum()
{
_numSps = new UISprite[NUM_SP];
// 数字做成prefab
_prefab = BundleMemManager.Instance.getPrefabByName(PathConst.FLOAT_BLOOD_NUM, EBundleType.eBundleCommon);
}

/// <summary>
/// 美术字体飘血
/// </summary>
/// <param name="isPlayer">是否是玩家</param>
/// <param name="hurtNum">伤害量</param>
/// <param name="parent">位置</param>
/// <param name="type">伤害类型枚举</param>
public void PlayFloatBlood(bool isPlayer, int hurtNum, Transform parent, eHurtType type = eHurtType.normalHurt)
{
if (isPlayer && (type == eHurtType.doubleHurt || type == eHurtType.normalHurt))
{
// 猜测为外观模式
Gate.instance.sendNotification(MsgConstant.MSG_FIGHT_COMBO); // comno 组合
}

GameObject parentObj = new GameObject();
parentObj.transform.position = parent.position;
parentObj.AddComponent("BillBoard");

GameObject obj = createBloodNum(isPlayer, hurtNum, type);
obj.transform.parent = parentObj.transform;
obj.transform.localPosition = Vector3.zero;
obj.transform.localScale = Vector3.one;
GameObject.Destroy(parentObj, 1.1f);
}

/// <summary>
/// 生成相应飘血对象
/// </summary>
/// <param name="isPlayer">是否是玩家</param>
/// <param name="hurtNum">伤害数量</param>
/// <param name="type">伤害类型</param>
private GameObject createBloodNum(bool isPlayer, int hurtNum, eHurtType type)
{
// 上面加载,这边实例化
GameObject obj = BundleMemManager.Instance.instantiateObj(_prefab);
_escapeSp = obj.transform.Find("escape").gameObject;
_doubleSp = obj.transform.Find("double").gameObject;
_withstandSp = obj.transform.Find("withstand").gameObject;
setTweenParam(obj, type);

switch(type)
{
case eHurtType.normalHurt:
case eHurtType.doubleHurt:
case eHurtType.goblinMoney:
case eHurtType.golbinPoison:
{
_escapeSp.SetActive(false);
_withstandSp.SetActive(false);
if (type == eHurtType.doubleHurt)
{
_doubleSp.SetActive(true);
}
else
_doubleSp.SetActive(false);

for(int i=1; i<=NUM_SP; i++)
{
UISprite sp = obj.transform.Find("sp"+i).GetComponent<UISprite>();
_numSps[i-1] = sp;
}

int numCount = getNumCount(hurtNum);
int firstNum = (NUM_SP - numCount - 1)/2; //第一个显示的符号位置,扣除一个符号位
for(int i=0; i<firstNum; i++)
_numSps[i].active = false;
for(int i=firstNum+numCount+1; i<NUM_SP; i++)
_numSps[i].active = false;
int index= -1;

for(int i=firstNum; i<=firstNum+numCount; i++)
{
_numSps[i].active = true;
_numSps[i].spriteName = getDigitSpName(isPlayer, hurtNum, index, numCount, type);

if(index == -1)
_numSps[i].transform.localScale = new Vector3(0.05f, 0.022f, 0.25f);
else
_numSps[i].transform.localScale = new Vector3(0.05f, 0.08f, 0.25f);
index++;

if (_numSps[i].spriteName.Equals("number_red_-"))
{
_doubleSp.transform.localPosition = _numSps[i].transform.localPosition + new Vector3(-3.8f,0,0);
}
}
break;
}
case eHurtType.withstandHurt:
case eHurtType.escapeHurt:
{
for(int i=1; i<=NUM_SP; i++)
{
GameObject spObj = obj.transform.Find("sp"+i).gameObject;
spObj.SetActive(false);
}
_doubleSp.SetActive(false);
if(type == eHurtType.escapeHurt)
{
_escapeSp.SetActive(true);
_withstandSp.SetActive(false);
}
else
{
_escapeSp.SetActive(false);
_withstandSp.SetActive(true);
}
break;
}
default:
break;
}
return obj;
}
}

Scenario

\Code\UI\Task\Scenario.cs

1
2
3
4
5
6
7
8
9
10
11
using UnityEngine;
using System.Collections;

public class SubScenario
{
public eDisplayType display; // 1表示剧情对话、2表示黑屏
public bool iconLeft; // 头像显示在左边,右边
public uint iconID; // NPC表示使用玩家的模型和姓名
public int delaySecond; // 对话持续时间(单位:秒),超过时间不操作,自动执行下一步
public string content; //具体对话内容
}

\Code\UI\Task\ScenarioManager.cs 剧情管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
using UnityEngine;
using System.Collections;
using NetGame;

public enum eTriggerType
{
notTrigger = 0,
acceptTask = 1, // 任务接取触发,NPC对话结束触发
finishTask = 2, // 任务完成触发,NPC对话结束触发
startGate = 3, // 关卡开始触发,进入关卡,人物出生完毕触发
finishGate = 4, // 关卡通关触发,最后一个怪物杀完,宝箱掉落之前
backToCity = 5 // 关卡回城触发,回到主城站在关卡就触发
}

public enum eDisplayType
{
useScenario = 1, // 关卡通关
blackSceen = 2 // 对话任务
}

public class ScenarioManager
{
private static ScenarioManager _instance = null;

public delegate void ScenarioOverHandler();
public bool passGateSuccess; // 关卡成功通关

private ScenarioOverHandler _scenarioOver; // 点击对话结束后的回调

private Hashtable _scenarioHash;
private Hashtable _taskIndexHash;

private GCReportScenario _reportScenario;

public static ScenarioManager Instance
{
get
{
if (_instance == null)
_instance = new ScenarioManager();
return _instance;
}
}

private ScenarioManager()
{
_scenarioHash = new Hashtable();
_taskIndexHash = new Hashtable();
_reportScenario = new GCReportScenario();
passGateSuccess = false;
}

// 对话点击结束
public void OnScenarioOver()
{
if (_scenarioOver != null)
{
_scenarioOver();
UIManager.Instance.showHiddenUI(UiNameConst.ui_scenario, true);
_scenarioOver = null;
}
}

// 将剧情保存为任务ID的索引
public void addTaskIndex(uint taskID, Scenario scenario)
{
BetterList<Scenario> scenarios;
if(_taskIndexHash.Contains(taskID))
scenarios = _taskIndexHash[taskID] as BetterList<Scenario>;
else
{
scenarios = new BetterList<Scenario>();
_taskIndexHash.Add(taskID, scenarios);
}
scenarios.Add(scenario);
}

// send to server
public void submitScenario(uint taskID, uint scenarioID, int step)
{
NetBase.GetInstance().Send(_reportScenario.ToBytes(taskID, scenarioID, step));
}

// reset all scenario
public void clearAllPlaying()
{
foreach (Scenario scenario in _scenarioHash.Values)
{
scenario.hadPlay = false;
scenario.CurrentStep = 0;
}
}

// 根据设置是否播放
public void setHadPlaying(uint taskID, uint scenarioID)
{
if(scenarioID > 0)
{
if(_taskIndexHash.Contains(taskID))
{
BetterList<Scenario> scenarios = _taskIndexHash[taskID] as BetterList<Scenario>;
foreach (Scenario scenario in scenarios)
{
if(scenario.scenarioID == scenarioID)
scenario.hadPlay = true;
}
}
}
}
}

NewBattleSystem

NewBattleSystem/BaseBuff.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class BaseBuff
{
// buffId,用来从配置文件读取
public int m_nBuffId;

public GameObject m_kSpecialEffectPre; // 触发特效
public GameObject m_kSpecialEffect; // buff特效

public Character m_kOwner; // 特效拥有者

public SkillEffectItem m_kItem;

protected float m_fParam;

protected float m_fTime = 0.0f;

public bool m_bActive; // buff是否激活

// 构造函数
public BaseBuff(int buffid, Character character, float param = 0.0f)
{
m_nBuffId = buffid;
m_kOwner = character;
m_fParam = param;

m_bActive = true;

// 从配置文件读取
m_kItem = ConfigDataManager.GetInstance().getSkillEffectConfig().getSkillEffectData(m_nBuffId);

Init();
}

public virtual void Init()
{
// 1、挂载触发特效
if (m_kItem.triggerEffectPre != null)
{
// 添加位置
Transform kAddPos = m_kOwner.getTagPoint("help_body");

// 加载特效预制体
GameObject asset = BundleMemManager.Instance.getPrefabByName(m_kItem.triggerEffectPre, EBundleType.eBundleBattleEffect);

// 实例化特效对象
m_kSpecialEffectPre = BundleMemManager.Instance.instantiateObj(asset, Vector3.zero, Quaternion.identity);

// 设置特效的位置和旋转信息
m_kSpecialEffectPre.transform.parent = kAddPos;

m_kSpecialEffectPre.transform.localPosition = Vector3.zero;

m_kSpecialEffectPre.transform.localRotation = Quaternion.identity;
}

// 2、挂载buff特效
if (m_kItem.effectPre != null)
{
Transform kAddPos = null;

switch (m_kItem.effPos)
{
case 0;
kAddPos = m_kOwner.getTagPoint("help_hp");
break;
case 1;
kAddPos = m_kOwner.getTagPoint("help_body");
break;
case 2;
kAddPos = m_kOwner.getTagPoint("shadow");
break;
case 3;
kAddPos = m_kOwner.getTagPoint("Footsteps");
break;
}

GameObject asset = BundleMemManager.Instance.getPrefabByName(m_kItem.effectPre, EBundleType.eBundleBattleEffect);

m_kSpecialEffect = BundleMemManager.Instance.instantiateObj(asset, Vector3.zero, Quaternion.identity);

m_kSpecialEffect.transform.parent = kAddPos;
m_kSpecialEffect.transform.localPosition = Vector3.zero;
m_kSpecialEffect.transform.localRotation = Quaternion.identity;
}

// 3、buff动画播放
switch(m_kItem.buffType)
{
case BUFF_TYPE.BT_XUANYUN:
{
if (m_kOwner.GetAI() != null)
{
// dizzy 眩晕
m_kOwner.GetAI().SendStateMessage(CharacterAI.CHARACTER_STATE.CS_DIZZY, m_kItem.lastTime);
}
}
break;
}
}

public virtual void Update(float delta)
{
if (m_bActive)
{
if (m_fTime > m_kItem.lastTime)
{
m_bActive = false;
Exit();

EventDispatcher.GetInstance().OnBuffDisappear(m_kItem.buffType);
return;
}
m_fTime += delta;
}
}

public virtual void Exit()
{
ReCalculateBuffProperty();

DestroySpecialEffect();

// 位运算
m_kOwner.m_nBuffState &= ~Bit((int)m_kItem.buffType);
}

public void DestroySpecialEffect()
{
if (m_kSpecialEffect != null)
{
GameObject.Destroy(m_kSpecialEffect);
m_kSpecialEffect = null;
}

if (m_kSpecialEffectPre != null)
{
GameObject.Destroy(m_kSpecialEffectPre);
m_kSpecialEffectPre = null;
}
}

public void ReCalculateBuffProperty()
{
m_kOwner.ReCalculateBuffProperty();
}

public static int Bit(int bit)
{
return 1 << bit;
}
}

Character</span>

\Character\Skill\Skill.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

// 技能容器类 抽象类
public abstract class Skill : MonoBehaviour
{
// 技能表现
protected SkillAppear cur_skill;
// 技能目标角色
protected Character skill_target = null;

protected bool hurt_protecting = false;
protected bool hurt_hide = false;

// 技能的拥有者
public Character m_kSkillOwner;

void Start()
{}

void Update ()
{}

abstract public SkillAppear GetCommonAttack();

abstract public bool IsSkillCommonActive();

public virtual SkillAppear GetCommon1() { return null; }
public virtual SkillAppear GetCommon2() { return null; }
public virtual SkillAppear GetCommon3() { return null; }
public virtual SkillAppear GetCommon4() { return null; }
public virtual SkillAppear GetCommon5() { return null; }
public virtual SkillAppear GetCommon6() { return null; }
public virtual SkillAppear GetCommon7() { return null; }
public virtual SkillAppear GetCommon8() { return null; }
public virtual SkillAppear GetCommon9() { return null; }
public virtual SkillAppear GetCommon10() { return null; }
public virtual SkillAppear GetCommon11() { return null; }
public virtual SkillAppear GetCommon12() { return null; }

public void SetSkillOwner(Character character)
{
m_kSkillOwner = character;
}

public void setCurrentSkill(SkillAppear skill)
{
cur_skill = skill;

if (skill == null)
{
return;
}
if (GetComponent<Character>().getType() == CharacterType.CT_PLAYER)
{

}
}
}

Character\CharacterBaseProperty.cs 角色属性分离出单独类,此为属性基类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using MVC.entrance.gate;
using manager;
using model;

public class CharacterBaseProperty
{
protected int hp; // 血量
public float move_speed; // 移动速度
public int hp_max; // 最大血量
public float attack_range; // 攻击范围
public int defence; // 防御力
public int attack_power; // 攻击力

// 两个基类互相保持对对方的引用
public Character property_owner;


public int level;
public int m_nFightPower;


}

Code\Common\CharacterProperty.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;
using NetGame;
using MVC.entrance.gate;
using manager;

public enum CHARACTER_CAREER
{
CC_BEGIN = 0,
CC_SWORD = 1, // 剑士
CC_ARCHER = 2, // 弓箭手
CC_MAGICIAN = 3, // 法师
CC_END
}

public class CharacterProperty : CharacterBaseProperty
{

}

public class CharacterAsset
{
public int diamond;
public int gold;

private int crystal;

/// <summary>
/// 水晶
/// </summary>
public int Crystal
{
get { return crystal; }
set { crystal = value; }
}

private int honor;

public int Honor
{
get { return honor; }
set { honor = value; }
}

public void SetDiamond(int value)
{
diamond = value;
}

public void SetGold(int value)
{
gold = value;
// 函数名前带On代表执行
EventDispatcher.GetInstance().OnPlayerAsset();
}
}

\Character\Character.cs 角色类继承MonoBehaviour,控制角色的位置,旋转,激活,面向方向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using manager;

public enum CharacterType
{
CT_NULL,
CT_PLAYER,
CT_MONSTER,
CT_PLAYERUI,
CT_PLAYEROTHER,
}

public enum SkillCanNotCastReason
{
SCNCR_CAST = 0, // 可以放
SCNCR_MP_NOT_ENOUGH, // 蓝不够
SCNCR_SKILL_IN_CD, // CD中
SCNCR_IS_DEAD, // 死亡
SCNCR_IN_SKILL_STATUS, // 技能状态
SCNCR_IN_CITY, // 主城中
SCNCR_NO_ENEMY // 没有敌人,并且技能不能平放
}

// 角色抽象基类
public abstract class Character : MonoBehaviour
{
protected CharacterType character_type = CharacterType.CT_NULL;

protected Vector3 velocity = Vector3.zero;
protected Vector3 face_dir = Vector3.forward;
protected Vector3 body_center = Vector3.zero;
protected Vector3 old_position = Vector3.zero;


private bool tryMoveTo(Vector3 pos)
{
bool bRet = canMoveTo(pos);

if (bRet)
{
transform.position = pos;
}
return bRet;
}

private void tryExtrudingTo(Vector3 pos)
{
RaycastHit hitInfo;
LayerMask mask = 1 << LayerMask.NameToLayer("Wall");
mask |= 1<< LayerMask.NameToLayer("obstacles");
Vector3 dir = pos - transform.position;
dir.Normalize();

if (Physics.Linecast(transform.position, pos + dir * 0.1f, out hitInfo, mask))
{
Vector3 normal = -hitInfo.normal;
Vector3 moveDir = pos - transform.position;
normal.Normalize();
moveDir.Normalize();

float cos_theta = Vector3.Dot(normal, moveDir);
Vector3 extrusionDir = moveDir - normal * cos_theta;

bool bCanMove = tryMoveTo(transform.position + extrusionDir * Vector3.Distance(pos, transform.position));

if (!bCanMove)
{
extrusionDir += hitInfo.normal * 0.1f;
tryMoveTo(transform.position + extrusionDir * Vector3.Distance(pos, transform.position));
}
}
}

public void setPosition()
{
bool bMoveRet = tryMoveTo(pos);

if (!bMoveRet)
{
tryExtrudingTo(pos);
}

transform.position = new Vector3(transform.position.x, 0.0f, transform.position.z);
}

public void movePosition(Vector3 deltaPos)
{
Vector3 curPos = getPosition();
setPosition(curPos + deltaPos);

if (m_kPet != null && deltaPos != Vector3.zero)
{
m_kPet.m_kMonsterPos.Add(transform.position);
}
}



public Vector3 getPosition()
{
return transform.position;
}

public void setFaceDir(Vector3 dir)
{
if (dir == Vector3.zero)
{
return;
}

face_dir = dir;
face_dir.y = 0.0f;
face_dir.Normalize();
transform.LookAt(transform.position + face_dir);
}

public bool canMoveTo(Vector3 pos)
{
RaycastHit hitInfo;
// i<<=j就是 i=i<<j
LayerMask mask = 1 << LayerMask.NameToLayer("Wall");

// i|=j就是i=i|j
mask |= 1 << LayerMask.NameToLayer("obstacles");

Vector3 dir = pos - transform.position;

dir.Normalize();

if (Physics.Linecast(transform.position, pos + dir * 0.1f, out hitInfo, mask))
{
return false;
}
return true;
}

public void setVisible(bool b)
{
Renderer[] renderChildren = GetComponentsInChildren<Renderer>();

foreach(Renderer renderChild in renderChildren)
{
renderChild.enabled = b;
}
}

public Transform getTagPoint(string name)
{
Transform[] allChildren = GetComponentsInChildren<Transform>();

foreach(Transform child in allChildren)
{
if (child.gameObject.name == name)
{
return child;
}
}
return null;
}
}

\Character\CharacterPlayer.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class CharacterPlayer : Character
{
public static CharacterPlayer sPlayerMe = null;

public static CharacterProperty character_property;



void Awake()
{
sPlayerMe = this;
}
}

\Character\CharacterMonster.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class CharacterMonster : Character
{
public MonsterProperty monster_property;
public MonsterAI monster_ai;
public MonsterArea owner_area;

// 多人副本怪Transform同步信息
public List<Vector3> m_kMultiMonsterSyncDir = new List<Vector3>();
public List<Vector3> m_kMultiMonsterSyncPos = new List<Vector3>();

public float m_fSyncFrequency = 0.0f;

void Awake()
{
init();
}

protected void Start ()
{
monster_ai.init(this);

skill.setHurtProtecting(true);

Renderer[] allRenderer = GetComponentsInChildren<Renderer>();

foreach(Renderer renderer in allRenderer)
{
// boss和精英怪需要上色
if (monster_property.GetSurfaceType() == MonsterProperty.MONSTER_SURFACE_LIGHT.MSL_BOSS)
{
renderer.sharedMaterial.SetFloat("_RimPower", (0.5f + 0.8f) * 0.7f);
renderer.sharedMaterial.SetFloat("_UseRim", 1.0f);
renderer.sharedMaterial.SetColor("_RimColor", new Color(255 / 255.0f, 0, 227 / 255.0f, 0));

EffectManager.Instance.CreateFX("Effect/Effect_Prefab/Monster/guangguan_BOSS", getTagPoint("shadow"))
}
}
}

}

NPC</span>

NPC系统和Task任务系统是紧密联系着的,还有UI系统,因为需要对话。

\Assets\Resources\Config\Data\NPC.xml NPC的配置文件

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RECORDS>
<RECORD ID="1001001" NPC_name="时光守护者" NPC_icon="cunzhang" NPC_model="Model/prefab/NPC/NPC_JiLan" MapID="100000001" PosXZ="-4.38,-3.42" RotationY="0" PathfindingXZ="-4.53,-2.4" DefaultWord="梦境是另一重人生,所看见、所感知的一切,呼吸、眼泪、痛苦以及欢乐,都是有意义的!" parentPos="20,-37,0" parentRotate="0,150,0" parentScale="1.5,1.5,1.5" scenarioPos="-1.06,-1.21,9" scenarioRotate="0,180,0" scenarioScale="0.9,0.9,0.9" cameraView="22.6" NPC_function="333"/>

<RECORD ID="1001002" NPC_name="雅典娜" NPC_icon="yadianna" NPC_model="Model/prefab/NPC/NPC_YaDianNa" MapID="100000001" PosXZ="-7.8,4.13" RotationY="90" PathfindingXZ="-6.7,4.13" DefaultWord="降临者!决不能放弃,只要一息尚存,就能创造奇迹!" parentPos="0,-44,0" parentRotate="0,165,0" parentScale="2,2,2" scenarioPos="-1.18,-1.64,9" scenarioRotate="0,168,0" scenarioScale="1.5,1.5,1.5" cameraView="22.6" NPC_function="333"/>

<RECORD ID="1001003" NPC_name="雷神·索尔" NPC_icon="lenshentuoer" NPC_model="Model/prefab/NPC/NPC_LeiShen" MapID="100000001" PosXZ="-0.83,-3.61" RotationY="-25" PathfindingXZ="-1.35,-2.5" DefaultWord="只有真正的英雄才能拿起雷神之锤!亵渎者将受到永恒的诅咒!" parentPos="0,-56,0" parentRotate="0,150,0" parentScale="2,2,2" scenarioPos="-1.12,-1.33,9" scenarioRotate="0,180,0" scenarioScale="1,1,1" cameraView="22.6" NPC_function="333"/>
</RECORDS>

\Assets\Code\Character\NPC\NPC.cs NPC游戏对象类,没有继承MonoBehaviour

这个类是交给NPCManager使用的,函数也是在那边调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
using UnityEngine;
using System.Collections;
using helper;

public class NPC
{
// 这些字段和XML中的是一致的,公有属性
public uint npcID;
public string npcName;
public string npcIcon;
public string npcModel;
public uint mapID;
public Vector3 npcPos;
public Vector3 rotatePosEuler;
public string defaultWord;
public Vector3 uiPos;
public Vector3 uiRotateEuler;
public Vector3 uiScale;
public Vector3 scenarioScale; //剧情缩放
public Vector3 scenarioPos; //剧情位置
public Vector3 scenarioRotate; //剧情旋转
public float caremeView;
public Vector3 pathLocatePos; //自动寻路的位置

// end

// 模型相关,私有字段
private GameObject _sceneNPC; //在场景中的组件,实例化的时候生成
private int _origLayer; //原来NPC的相关信息
private Vector3 _origPos; //在场景的原始位置
private Quaternion _origRotate;
private Vector3 _origScale;
private Transform _origParent;

// NPC带有任务
private GameObject _markNameRoot; //挂接名称跟任务指引
private string _taskStateTag; //当前任务的标记
private GameObject _taskTagObj; //任务标记的对象
private Vector3 _tagPos; //标记的位置
private Vector3 _tagScale;
private float _particleSize; //粒子大小
private Vector3 _faceDir; //人物的朝向
private NPCAction _action; // NPC的行为,专门一个继承MonoBehaviour的类处理

Transform _modelRoot;
GameObject obj;

public Vector3 FaceDir
{
get { return _faceDir; }
}

public NPCAction Action
{
get { return _action; }
}

// 因为不继承MonoBehaviour,所有Awake函数不能启动,也无法使用transform.FindChild这类方法,所以作者给注释掉了
private void Awake()
{
//_modelRoot = transform.FindChild("ui_task_dialog/Background/empty");
//obj = Instantiate(_modelRoot.gameObject) as GameObject;
//obj.transform.parent = _modelRoot;
//obj.transform.localScale = Vector3.one;
//obj.transform.localPosition = Vector3.zero;
//obj.transform.name = "pos";
}

//实例化自身,对外提供的接口
// 返回游戏对象
// 参数是加载的资源
public GameObject instanciateSelf(Object asset)
{
if(_sceneNPC == null) //如果没有初始过NPC
{
if (asset == null)
return null;

// 实例化
_sceneNPC = BundleMemManager.Instance.instantiateObj(asset, npcPos, Quaternion.identity);
_sceneNPC.transform.localEulerAngles = rotatePosEuler;
// NPCAction这个组件是动态添加的
NPCAction action = _sceneNPC.AddComponent<NPCAction>();

// NPCAction有NPC类的引用,两者进行交互
action.OwnerNPC = this;
_action = action;
// 给Hierarchy的游戏对象赋名
_sceneNPC.name = npcName;
// 正对寻路点,即正对玩家
_faceDir = npcPos - pathLocatePos;
_faceDir.Normalize();
// name_mark就是头顶的BillBoard,显示NPC的名字
// 具体看下面的图片
_markNameRoot = _sceneNPC.transform.Find("name_mark").gameObject;

// 自带了name_mark
if(_markNameRoot != null)
{
_markNameRoot.AddComponent<BillBoard>();

// 使用NGUI把名字换成配置表里的名字
UILabel nameLabel = _markNameRoot.GetComponentInChildren<UILabel>();
nameLabel.text = npcName;

// NPC的任务
_taskStateTag = NPCManager.SIMBOL_ACCENT; // 橙色叹号“TanHao_cheng”,是个const string字段
// 得到这个橙色叹号的信息
_taskTagObj = _markNameRoot.transform.Find(_taskStateTag).gameObject;
_tagPos = _taskTagObj.transform.localPosition;
_tagScale = _taskTagObj.transform.localScale;

// glow 辉光 带了粒子系统
ParticleSystem particle = _taskTagObj.GetComponentInChildren<ParticleSystem>();

if(particle != null)
// ParticleSystem.startSize 发射时粒子的初始大小
// 已过时,使用ParticleSystem.MainModule.startSize
_particleSize = particle.startSize;
else
_particleSize = 2f;
//打上任务标记
tagTaskState(null);
}
}
// 不为空直接返回
return _sceneNPC;
}

// 任务从场景到UI还是从UI到场景,对外提供的接口
// 参数是
public void changeNPCLayer(bool sceneToUI)
{
// 场景到UI
if(sceneToUI)
{

}
else
{

}
}

private void Update()
{

}
}

这个NPC带了一个橙色的叹号

\Assets\Code\Character\NPC\NPCAction.cs NPC的行为,比如监听鼠标按下才行动,需要继承MonoBehaviour

像FindChild().GetComponent<>(),SetActice()需要用到这种的,要继承MonoBehaviour

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
using UnityEngine;
using System.Collections;
using manager;

public class NPCAction : MonoBehaviour
{
public static bool sClickNPC = false; // 是否点击了这个NPC
const int TASK_LEN = 3; // 这个NPC的任务数量
public const int AWARD_LEN = 5; // 奖励数量

private NPC _ownerNPC; // NPC类,互相交互

public NPC OwnerNPC
{
set {_ownerNPC = value;}
}

private UILabel _npcName; // NPC名称
private GameObject _NPCAvasta; // 显示NPC的UI
private GameObject _awardLbl; // 奖励标题
private GameObject _awardBg;
private GameObject[] _taskList; // 任务清单,数组
private GameObject[] _awardList; // 奖励清单,数组
private UILabel _descriptionLbl; // 对话框内容
private GameObject _dialogBtn; // 对话点击按钮

// Unity自带函数,监听鼠标按下
void OnMouseDown()
{
if (GuideManager.Instance.IsEnforceUI)
{
return;
}

if (!Global.pressOnUI())
{
sClickNPC = true;
if (checkDistanceTrigger()) // 在可触发范围,谁和谁的触发范围?当然是玩家角色和这个NPC
{
Vector3 faceDir = _ownerNPC.npcPos - CharacterPlayer.sPlayerMe.getPosition();
// Vector3中normalized和Normalize函数的区别
// 1.Vector3.normalized
// Returns this vector with a magnitude of 1 (Read Only)
// 2.Vector3.Normalize()
// Makes this vector have a magnitude of 1.

// 它们的区别和共同点是:

// 共同点:实现规范化,让一个向量保持相同的方向,但它的长度为1.0,如果这个向量太小而不能被规范化,一个零向量将会被返回。

// 不同点:Vector3.normalized的特点是当前向量是不改变的并且返回一个新的规范化的向量;Vector3.Normalize的特点是改变当前向量,也就是当前向量长度是1
faceDir.Normalize();

// 一旦进入和NPC的触发范围,就让玩家角色面对NPC,然后弹出对话框
CharacterPlayer.sPlayerMe.setFaceDir(faceDir);
showTaskDialog();
}
else
{
TaskManager.Instance.isTaskFollow = true; //也跟点击任务追踪一样
Vector3 moveToPos = _ownerNPC.pathLocatePos; // 即上图中的“xunludian”(我自己写的),猜测是配置文件中的PathfindingXZ字段

// 处理玩家角色到这个点
CharacterPlayer.sPlayerMe.GetAI().m_kPursueState.m_bGotoGate = false;
CharacterPlayer.sPlayerMe.GetAI().m_kPursueState.m_kPursurNPC = _ownerNPC;
CharacterPlayer.sPlayerMe.GetAI().SendStateMessage(CharacterAI.CHARACTER_STATE.CS_PURSUE, moveToPos);
// 这是作者注释掉的
//PlayerManager.Instance.agent.SetDestination(moveToPos);
//PlayerManager.Instance.pathFind.beginMove(_ownerNPC);
}
}
}

// 对外提供的接口
// 走到一定范围才显示task dialog,isClickFollow是否点击自动寻路,点击自动寻路就进入第二个页面
public void showTaskDialog(bool isClickFollow = false)
{
_taskList = new GameObject[TASK_LEN];
_awardList = new GameObject[AWARD_LEN];

// public const string ui_task_dialog = "ui_task_dialog";
// 界面显示交由UIManager处理了
UIManager.Instance.openWindow(UiNameConst.ui_task_dialog);

//根据UI名称获取相应的Object
GameObject obj = UIManager.Instance.getUIFromMemory(UiNameConst.ui_task_dialog);

setTasks(obj);

// 接受任务的NPC和提交任务的NPC不一定是同一个
if(isClickFollow && TaskManager.Instance.followTask != null&&
(_ownerNPC.npcID.Equals(TaskManager.Instance.followTask.submitNPCID)||_ownerNPC.npcID.Equals(TaskManager.Instance.followTask.acceptNPCID))
)
{
showTask(TaskManager.Instance.followTask);
}
}


// 设置该NPC身上的任务
// 参数是UI对象
void setTasks(GameObject obj)
{
// 任务从任务系统得到,传入npcID,得到任务列表
BetterList<Task> tasks = TaskManager.Instance.getTaskList(_ownerNPC.npcID);
// 准备遍历任务
int i = 0;
string listPrefix = "npc_task";

if(tasks.size > 0)
{
foreach (Task task in tasks)
{
if(i < TASK_LEN)
{
if(task != null)
{
// _taskList是自己维护的任务列表
_taskList[i] = obj.transform.Find("Panel/dialog/taskList/"+listPrefix+(i+1)).gameObject;

enableCollider(_taskList[i], true);
// 看图,都带了BtnClickTask和UILabel脚本
BtnClickTask click = _taskList[i].GetComponent<BtnClickTask>();
UILabel titleLbl = _taskList[i].GetComponentInChildren<UILabel>();
titleLbl.text = TaskManager.Instance.setTaskTitle(task, false);
click.RelateTask = task;
click.NpcAction = this;
i++;
}
}
else
break;
}
}

if(i < TASK_LEN) //不足三条要隐藏
{
for(int j = i; j < TASK_LEN; j++)
{
_taskList[j] = obj.transform.Find("Panel/dialog/taskList/"+listPrefix+(j+1)).gameObject;
enableCollider(_taskList[j], false);
}
}
// 设置NPC的名称
_npcName = obj.transform.Find("Panel/title/Label").GetComponent<UILabel>();
_npcName.text = _ownerNPC.npcName;

// 设置NPC的描述
_descriptionLbl = obj.transform.Find("Panel/dialog/content/description").GetComponent<UILabel>();
_descriptionLbl.text = _ownerNPC.defaultWord;

_dialogBtn = obj.transform.Find("Panel/dialog/taskList/npcDialog").gameObject;
enableCollider(_dialogBtn, false);

_awardLbl = obj.transform.Find("Panel/dialog/content/Label").gameObject;
_awardBg = obj.transform.Find("Panel/dialog/content/awardBg").gameObject;
// 把奖励面板设置为不显示 begin
_awardLbl.SetActive(false);
_awardBg.SetActive(false);

for(i = 0; i < AWARD_LEN; i++)
{
string awardPrefix = "award";
_awardList[i] = obj.transform.Find("Panel/dialog/content/"+awardPrefix+(i+1)).gameObject;
_awardList[i].SetActive(false);
}

// end
// 任务从场景到UI
_ownerNPC.changeNPCLayer(true);
}

//显示奖励面板
// 参数是任务对象
public void showTask(Task task)
{
int len = task.rewardNum < AWARD_LEN ? task.rewardNum : AWARD_LEN;

// 显示奖励面板
if(len > 0)
{
_awardLbl.SetActive(true);
_awardBg.SetActive(true);
}
// 定义索引,准备遍历
int index = 0;
// rewardItems 奖励物品
for (int i = 0; i < task.rewardItems.size; i++)
{
if (index < len)
{
_awardList[index].SetActive(true);
UITexture texture = _awardList[index].GetComponentInChildren<UITexture>();
texture.enabled = true;
UISprite icon = _awardList[index].transform.FindChild("gold").GetComponent<UISprite>();
UILabel label = _awardList[index].GetComponentInChildren<UILabel>();
// 设置UI界面
// ItemManager 物品管理类
ItemTemplate item = ItemManager.GetInstance().GetTemplateByTempId((uint)task.rewardItems[i].Id);
_awardList[index].transform.FindChild("icon").GetComponent<BtnTipsMsg>().Iteminfo = new ItemInfo((uint)task.rewardItems[i].Id,0,0);
DealTexture.Instance.setTextureToIcon(texture, item, false);
UISprite boder = _awardList[index].transform.FindChild("bg").GetComponent<UISprite>();
// BagManager 背包管理类
boder.spriteName = BagManager.Instance.getItemBgByType(item.quality, true);
icon.alpha = 0;
label.text = Constant.NUM_PREFIX + task.rewardItems[i].Value.ToString();
index++;

index++;
}
}
// rewardRes 奖励金钱
for (int i = 0; i < task.rewardRes.size; i++)
{
if (index < len)
{
// 显示面板
_awardList[index].SetActive(true);
// 根据奖励的金钱对UI进行处理
UITexture texture = _awardList[index].GetComponentInChildren<UITexture>();
UISprite icon = _awardList[index].transform.FindChild("gold").GetComponent<UISprite>();
UILabel label = _awardList[index].GetComponentInChildren<UILabel>();
texture.mainTexture = null;
texture.enabled = false;
UISprite boder = _awardList[index].transform.FindChild("bg").GetComponent<UISprite>();
boder.spriteName = BagManager.Instance.getItemBgByType(eItemQuality.eOrange, true);
icon.alpha = 1;
icon.spriteName = SourceManager.Instance.getIconByType((eGoldType)task.rewardRes[i].Id);
label.text = Constant.NUM_PREFIX + task.rewardRes[i].Value.ToString();
index++;
}
}

// 处理完关闭
for (int i = index; i < _awardList.Length; i++)
{
_awardList[i].SetActive(false);
}

enableCollider(_dialogBtn, true); //对话按钮监听鼠标

for(int i=0; i<TASK_LEN; i++)
{
enableCollider(_taskList[i], false); //任务按钮隐藏监听
}

if(TaskManager.Instance.isTaskDialogNotOver(task))
{
// 从任务系统管理器得到对话内容,
// 任务系统管理器是从Task对象获取到的
string[] dialogs = TaskManager.Instance.getTaskDialog();
_descriptionLbl.text = dialogs[0];
_dialogBtn.GetComponentInChildren<UILabel>().text = dialogs[1];
}
}

// 设置对象是否可以碰撞
// 参数 BoxCollider所属的对象,是否可以碰撞
private void enableCollider(GameObject obj, bool enable)
{
BoxCollider boxCollider = obj.GetComponent<BoxCollider>();

boxCollider.enabled = enable;
obj.SetActive(enable);
}

// 计算NPC和人物的距离是否可触发
private bool checkDistanceTrigger()
{
// NPC的位置
Vector2 npcPos = new Vector2(_ownerNPC.npcPos.x, _ownerNPC.npcPos.z);
// 玩家角色的位置
Vector2 playerPos = new Vector2(CharacterPlayer.sPlayerMe.getPosition().x, CharacterPlayer.sPlayerMe.getPosition().z);

// 寻路点的位置
Vector2 locatePos = new Vector2(_ownerNPC.pathLocatePos.x, _ownerNPC.pathLocatePos.z);
// NPC和玩家角色的位置 < NPC和寻路点的位置 + 0.1
return Vector2.Distance(npcPos, playerPos) <= Vector2.Distance(npcPos, locatePos) + 0.1;
}

public NPC OwnerNPC
{
set { _ownerNPC = value; }
}
}

ui_task_dialog

\Assets\Code\UI\Common\ReadLocclData\DataReadXml.cs 读取帮助,制作,NPC位置,任务,剧情的XML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
using helper;
using manager;
using model;

using System;
using UnityEngine;

// NPC配置文件解析
public class DataReadNPC : DataReadBase
{
public override string getRootNodeName ()
{
return "RECORDS";
}

/// <summary>
/// 对外提供的接口,调用这个函数,传三个参数,就可以给NPC对象赋值
/// 多次调用,猜测调用的地方是循环
/// </summary>
/// <param name="key"></param>
/// <param name="name"></param>
/// <param name="value"></param>
public override void appendAttribute (int key, string name, string value)
{
NPC npc;

string[] splits = null;

char[] charSeparators = new char[] {','};

uint hashKey = (uint)key;

if (NPCManager.Instance.NpcHash.Contains (hashKey))
{
npc = NPCManager.Instance.NpcHash [hashKey] as NPC;
}
else
{
npc = new NPC ();
NPCManager.Instance.NpcHash.Add (hashKey, npc);
}

switch (name)
{
case "ID":
npc.npcID = uint.Parse (value);
break;
case "NPC_name":
npc.npcName = value;
break;
case "NPC_icon":
npc.npcIcon = value;
break;
case "NPC_model":
npc.npcModel = value;
break;
case "MapID":
npc.mapID = uint.Parse (value);
break;
case "PosXZ": // PosXZ="-4.38,-3.42"
splits = value.Split (charSeparators);
npc.npcPos = new Vector3 (float.Parse (splits [0]), 0f, float.Parse (splits [1]));
break;
case "RotationY":
npc.rotatePosEuler = new Vector3 (0f, float.Parse (value), 0f);
break;
case "DefaultWord":
npc.defaultWord = value;
break;
case "parentPos": // parentPos="20,-37,0"
splits = value.Split (charSeparators);
npc.uiPos = new Vector3 (float.Parse (splits [0]), float.Parse (splits [1]), float.Parse (splits [2]));
break;
case "parentRotate":
splits = value.Split (charSeparators);
npc.uiRotateEuler = new Vector3 (float.Parse (splits [0]), float.Parse (splits [1]), float.Parse (splits [2]));
break;
case "parentScale":
splits = value.Split (charSeparators);
npc.uiScale = new Vector3 (float.Parse (splits [0]), float.Parse (splits [1]), float.Parse (splits [2]));
break;
case "scenarioScale":
splits = value.Split (charSeparators);
npc.scenarioScale = new Vector3 (float.Parse (splits [0]), float.Parse (splits [1]), float.Parse (splits [2]));
break;
case "scenarioPos":
splits = value.Split (charSeparators);
npc.scenarioPos = new Vector3 (float.Parse (splits [0]), float.Parse (splits [1]), float.Parse (splits [2]));
break;
case "scenarioRotate":
splits = value.Split (charSeparators);
npc.scenarioRotate = new Vector3 (float.Parse (splits [0]), float.Parse (splits [1]), float.Parse (splits [2]));
break;
case "cameraView":
npc.caremeView = float.Parse (value);
break;
case "PathfindingXZ":
splits = value.Split (charSeparators);
npc.pathLocatePos = new Vector3 (float.Parse (splits [0]), 0f, float.Parse (splits [1]));
break;
default:
break;
}
}
}

\Code\Character\NPC\NPCManager.cs 管理NPC对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
using UnityEngine;
using System.Collections;

public class NPCManager
{
public const string SIMBOL_ACCENT_FAIL = "TanHao_hui"; // 主线任务等级不满足
public const string SIMBOL_ACCENT = "TanHao_cheng"; // 可领取阶段
public const string SIMBOL_PROCESSING = "WenHao_hui"; // 进行中
public const string SIMBOL_COMPLETE = "WenHao_cheng"; // 已完成

public NPC openNPC; // 当前打开的NPC
public NPC mainTaskNPC; // 主任务挂载的NPC
public NPC pathFollowNPC; // 任务追踪的NPC
public bool hasInstantiate; // 是否已经实例化过了

// 单例模式
private static NPCManager _instance = null;

private Hashtable _npcHash; // 以NPC ID作为索引
private BetterList<GameObject> _instanceNPCs; // NPC的实例化

private Camera _modelCamera; // 模型摄像机
private int _initCount;

public Hashtable NpcHash
{
get { return _npcHash; }
}

public Camera ModelCamera
{
get
{
if(_modelCamera == null)
createCamera();
return _modelCamera;
}
}

public static NPCManager Instance
{
get
{
if(_instance == null)
_instance = new NPCManager();
return _instance;
}
}

// 构造函数里初始化数据
private NPCManager()
{
_npcHash = new Hashtable();
_instanceNPCs = new BetterList<GameObject>();
hasInstantiate = false;
openNPC = null;
mainTaskNPC = null;
_modelCamera = null;
}

//initiate the model camera
/// <summary>
/// 对外提供的接口,创建模型摄像机
/// </summary>
/// <param name="create">创建或销毁</param>
public void createCamera(bool create = true)
{
if (create)
{
if (_modelCamera == null) // 如果需要创建且模型摄像机不存在
{
//先加载
GameObject asset = BundleMemManager.Instance.getPrefabByName(PathConst.MODEL_CAMERA, EBundleType.eBundleCommon);
// 再实例化
GameObject obj = BundleMemManager.Instance.instantiateObj(asset, new Vector3(0, 0, -10), Quaternion.identity);
// 得到Camera组件
_modelCamera = obj.GetComponent<Camera>();
}
}
else if (_modelCamera != null)
{
Object.Destroy(_modelCamera.gameObject);
_modelCamera = null;
}
}

// 初始化各个NPC
public void initNPC(int mapID)
{
BetterList<NPC> npcs = getNPCs((uint)mapID);
if(_instanceNPCs.size > 0)
{
for(int i=0; i<_instanceNPCs.size; i++)
{
GameObject obj = _instanceNPCs[i];
GameObject.Destroy(obj);
}
}
_instanceNPCs.Clear();
_initCount = 0;

if(npcs.size > 0)
{
initSingleNPC(npcs);
}
hasInstantiate = true;
}

// 获取在地图中的所有NPC
private BetterList<NPC> getNPCs(uint mapID)
{
BetterList<NPC> npcs = new BetterList<NPC>();
foreach (NPC npc in _npcHash.Values)
{
if(npc.mapID == mapID)
npcs.Add(npc);
}
return npcs;
}

// 实例化NPC
private void initSingleNPC(BetterList<NPC> npcs)
{
if (_initCount >= npcs.size)
return;

NPC npc = npcs[_initCount];

if (BundleMemManager.Instance.isTypeInCache(EBundleType.eBundleNPC))
{
BundleMemManager.Instance.loadPrefabViaWWW<GameObject>(
EBundleType.eBundleNPC,
npc.npcModel,
(asset) =>
{
GameObject newNPC = npc.instanciateSelf(asset);
if (newNPC != null)
{
_instanceNPCs.Add(newNPC);
markNPCTag(npc);
}
_initCount++;
initSingleNPC(npcs);
}
);
}
else
{
GameObject asset = BundleMemManager.Instance.getPrefabByName(npc.npcModel, EBundleType.eBundleNPC);
GameObject newNPC = npc.instanciateSelf(asset);

if (newNPC != null)
{
_instanceNPCs.Add(newNPC);
markNPCTag(npc);
}
_initCount++;
initSingleNPC(npcs);
}
}

//对话点击完毕设置各个NPC的状态
public void markNPCTag(NPC npc)
{
if(npc == null || npc.mapID != CharacterPlayer.character_property.getServerMapID()) //不在一个主城不需要打Tag
return;

if(SceneManager.Instance.currentScene == SCENE_POS.IN_CITY) //只有主城的NPC才打tag
{
BetterList<Task> tasks = TaskManager.Instance.getTaskList(npc.npcID);

if(tasks.size == 0)
npc.tagTaskState(null); //无任务清空标记
else
{
Task task = tasks[0];
if(task.taskLine == TaskManager.MAIN_TASK_LINE)
mainTaskNPC = npc;
if(tasks.size > 1) //多余一条任务才需要判断
{
Task[] compareTasks = new Task[4];
for(int i=0; i<4; i++)
compareTasks[i] = null;
foreach(Task eachTask in tasks) //遍历打标记的任务
{
if(eachTask.taskState == eTaskState.eFinish)
{
if(compareTasks[0] == null)
compareTasks[0] = eachTask;
}
else if(eachTask.taskState == eTaskState.eCanAccept)
{
if(compareTasks[1] == null)
compareTasks[1] = eachTask;
}
else if(eachTask.taskState == eTaskState.eInProgress)
{
if(compareTasks[2] == null)
compareTasks[2] = eachTask;
}
else
{
if(compareTasks[3] == null)
compareTasks[3] = eachTask;
}
}
for(int i=0; i<4; i++)
{
if(compareTasks[i] != null)
{
task = compareTasks[i];
break;
}
}
}
npc.tagTaskState(TaskManager.Instance.getTagMark(task));
}
}
}
}

Manager

\Manager\EventDispatcher.cs 定义了一堆Public委托,然后提供了执行委托的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
using UnityEngine;
public class EventDispatcher
{
public enum eEventType
{
MOUSE_CLICK
}

private static EventDispatcher instance;

public static EventDispatcher GetInstance()
{
if (instance == null)
{
instance = new EventDispatcher();
}
return instance;
}

/// <summary>
/// 地图选择
/// </summary>
/// <param name="mapData"></param>
public delegate void EventMapSelect(GameObject mapData);

/// <summary>
/// 地图选中事件
/// </summary>
public event EventMapSelect MapSelect;

public void OnMapSelect(GameObject mapData)
{
if (MapSelect != null)
{
MapSelect(mapData);
}
}

//从WWW数据获取数据的回调
public delegate void EventLoadFromWWW(Object obj);

/// <summary>
/// 碰撞事件
/// </summary>
/// <param name="c"></param>
public delegate void CollisionHandler(Collision c);

public event CollisionHandler CollisionEnter;

void OnCollisionEnter(Collision c)
{
if (CollisionEnter != null)
{
CollisionEnter(c);
}
}

public event CollisionHandler CollisionExit;

void OnCollisionExit(Collision c)
{
if (CollisionExit != null)
{
CollisionExit(c);
}
}

/// <summary>
/// 副本等级选择
/// </summary>
/// <param name="level"></param>
public delegate void EventSelectFightLevel(Global.eFightLevel level);

/// <summary>
/// 选中关卡挑战难度
/// </summary>
public event EventSelectFightLevel SelectFightLevel;

public void OnSelectFightLevel(Global.eFightLevel level)
{
if (SelectFightLevel != null)
{
SelectFightLevel(level);
}
}

/// <summary>
/// 角色数据变化
/// </summary>
/// <param name="player"></param>
public delegate void EventPlayerProperty();

/// <summary>
/// 角色变化通知
/// </summary>
public event EventPlayerProperty PlayerProperty;

public void OnPlayerProperty()
{
if (PlayerProperty != null)
{
PlayerProperty();
}
}

/// <summary>
/// 角色资产信息
/// </summary>
public delegate void EventPlayerAsset();

/// <summary>
/// 资产变化
/// </summary>
public event EventPlayerAsset PlayerAsset;

public void OnPlayerAsset()
{
if (PlayerAsset != null)
{
PlayerAsset();
}
}

/// <summary>
/// 角色等级
/// </summary>
public delegate void EventPlayerLevel();

/// <summary>
/// 等级变化
/// </summary>
public event EventPlayerLevel PlayerLevel;

public void OnPlayerLevel()
{
if (PlayerLevel != null)
{
PlayerLevel();
}
}

/// <summary>
/// 连接服务器回调
/// </summary>
public delegate void EventConnectedServer();

/// <summary>
/// 成功连接服务器回调
/// </summary>
public event EventConnectedServer ConnectedServer;

public void OnConnectedServer()
{
if (ConnectedServer != null)
{
ConnectedServer();
}
}

/// <summary>
/// 丢失连接回调
/// </summary>
public delegate void EventLostConnectServer();

/// <summary>
/// 丢失连接
/// </summary>
public event EventLostConnectServer LostConnectServer;

public void OnLostConnectServer()
{
if (LostConnectServer != null)
{
LostConnectServer();
}
}

/// <summary>
/// 战斗中进度条变化
/// </summary>
/// <param name="value"></param>
public delegate void HandleFightChangeSlider(int value);

/// <summary>
/// 战斗中hp变化
/// </summary>
public event HandleFightChangeSlider FightChgangeHP;

public void OnFightChangeHP(int value)
{
if (FightChgangeHP != null)
{
FightChgangeHP(value);
}
}

/// <summary>
/// 战斗中mp变化
/// </summary>
public event HandleFightChangeSlider FightChangMP;

public void OnFightChangeMP(int value)
{
if (FightChangMP != null)
{
FightChangMP(value);
}
}

/// <summary>
/// 战斗中exp变化
/// </summary>
public event HandleFightChangeSlider FightChangeEXP;

public void OnFightChangeEXP(int value)
{
if (FightChangeEXP != null)
{
FightChangeEXP(value);
}
}

/// <summary>
/// 对话框确认操纵
/// </summary>
/// <param name="type"></param>
public delegate void HandleDialogSure(eDialogSureType type);

/// <summary>
/// 监听对话框确认按钮
/// </summary>
public event HandleDialogSure DialogSure;

public void OnDialogSure(eDialogSureType type)
{
if (DialogSure != null)
{
DialogSure(type);
}
}

/// <summary>
/// 监听对话框取消按钮
/// </summary>
public delegate void HandleDialogCancel(eDialogSureType type);

public event HandleDialogCancel DialogCancel;

public void OnDialogCancel(eDialogSureType type)
{
if (DialogCancel != null)
{
DialogCancel(type);
}
}

public delegate void HandleOpenFunc(bool isOpen);
public HandleOpenFunc EventOpenFunc;

public void OnOpenFunc(bool isOpen)
{
if (EventOpenFunc != null)
{
EventOpenFunc(isOpen);
}
}


/// <summary>
/// 窗口名称
/// </summary>
/// <param name="name"></param>
public delegate void HandleWindowName(string name);

public HandleWindowName EventWindowName;

public void OnSetWindowName(string name)
{
if (EventWindowName != null)
{
EventWindowName(name);
}
}

public delegate void EventCloseOtherWindow();

public EventCloseOtherWindow CloseOtherWindow;

public void OnCloseOtherWindow()
{
if (null != CloseOtherWindow)
{
CloseOtherWindow();
}
}

// 成就监听
public delegate void HandleMissionReceiveChange(bool bReceive);

// 成就相关的监听
public event HandleMissionReceiveChange MissionReceiveChange;

public void OnMissionReceiveChange(bool bRecev)
{
if (MissionReceiveChange != null)
{
MissionReceiveChange(bRecev);
}
}

// buff 回调
public delegate void HandleBuffDisappear(BUFF_TYPE buffType);

public event HandleBuffDisappear BuffDisappear;

public void OnBuffDisappear(BUFF_TYPE type)
{
if (BuffDisappear != null)
{
BuffDisappear(type);
}
}

/// <summary>
/// 寻路点寻到回调
/// </summary>
public delegate void HandlePathFindingArrived();

public event HandlePathFindingArrived PathFindingArrived;

public void OnPathFindingArrived()
{
if (PathFindingArrived != null)
{
PathFindingArrived();
}
}

//
public delegate void HandleAttackActived();

public event HandleAttackActived AttackActived;

public void OnAttackActived()
{
if (AttackActived != null)
{
AttackActived();
}
}

//
public delegate void HandleHUDNeedHideShow(bool bVisible);

public event HandleHUDNeedHideShow HUDNeedHideShow;

public void OnHUDNeedHideShow(bool bVisible)
{
if (HUDNeedHideShow != null)
{
HUDNeedHideShow(bVisible);
}
}
}

\Code\Manager\BattleManager.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using MVC.entrance.gate;
using NetGame;

public enum DAMAGE_TYPE
{
DT_COMMON_DAMAGE = 0, // 一般的伤害
DT_WITH_STAND,
DT_BLAST_DAMAGE, // 暴击带来的伤害
DT_BUFF_DAMAGE, // buff 带来的伤害
}

public enum DAMAGE_TIMES
{
DT_ONCE = 1,
DT_MULTI,
}

public enum COMBAT_SKILL_RESULT
{
CSR_HIT = 0,
CSR_MISS,
CSR_PARRY, // 格挡
CSR_DODGE, // 闪避
}

public class BattleManager
{
private SkillTargetFlag _skill_target_flag;

public bool m_bTimeOver = false; // 战斗时间是否结束

AudioClip clip_broken;

float m_fMinDamageCoeffic = 0.2f; // 最低攻击系数
float m_fBaseDamageLow = 0.9f; // 伤害浮动区间
float m_fBaseDamageUp = 1.1f; // 伤害浮动区间
float m_fMinDamageNum = 1.0f; // 攻击最低伤害值

float m_fMinzhongBase = 0.95f;
float m_fMinzhongAnti = 0.45f;
float m_fMinzhongInc = 0.05f;

float m_fBlastBase = 0.1f;
float m_fBlastAnti = 0.1f;
float m_fBlastInc = 0.4f;

float m_fBlastDamageBase = 2.0f;
float m_fBlastDamageAnti = 1.0f;
float m_fBlastDamageInc = 1.0f;

float m_fZhaojiaBase = 0.0f;
float m_fZhaojiaAnti = 0.0f;
float m_fZhaojiaInc = 0.5f;

float m_fZhaojiaDamageAnti = 0.5f;
float m_fZhaojiaDamageRef = 0.5f;

private static BattleManager _instance = null;

public static BattleManager Instance
{
get
{
if(_instance == null)
_instance = new BattleManager();
return _instance;
}
}

private BattleManager()
{
m_fMinDamageCoeffic = ConfigDataManager.GetInstance ().GetPublicDataConfig ().getPublicData (9001001).type7Data;
m_fBaseDamageLow = ConfigDataManager.GetInstance ().GetPublicDataConfig ().getPublicData (9001002).type8List[0];
m_fBaseDamageUp = ConfigDataManager.GetInstance ().GetPublicDataConfig ().getPublicData (9001002).type8List[1];
m_fMinDamageNum = ConfigDataManager.GetInstance ().GetPublicDataConfig ().getPublicData (9001003).type7Data;

m_fMinzhongBase = ConfigDataManager.GetInstance().GetPublicDataConfig().getPublicData(9001004).type8List[0];
m_fMinzhongAnti = ConfigDataManager.GetInstance().GetPublicDataConfig().getPublicData(9001004).type8List[1];
m_fMinzhongInc = ConfigDataManager.GetInstance().GetPublicDataConfig().getPublicData(9001004).type8List[2];

m_fBlastBase = ConfigDataManager.GetInstance().GetPublicDataConfig().getPublicData(9001005).type8List[0];
m_fBlastAnti = ConfigDataManager.GetInstance().GetPublicDataConfig().getPublicData(9001005).type8List[1];
m_fBlastInc = ConfigDataManager.GetInstance().GetPublicDataConfig().getPublicData(9001005).type8List[2];

m_fBlastDamageBase = ConfigDataManager.GetInstance().GetPublicDataConfig().getPublicData(9001006).type8List[0];
m_fBlastDamageAnti = ConfigDataManager.GetInstance().GetPublicDataConfig().getPublicData(9001006).type8List[1];
m_fBlastDamageInc = ConfigDataManager.GetInstance().GetPublicDataConfig().getPublicData(9001006).type8List[2];

m_fZhaojiaBase = ConfigDataManager.GetInstance().GetPublicDataConfig().getPublicData(9001007).type8List[0];
m_fZhaojiaAnti = ConfigDataManager.GetInstance().GetPublicDataConfig().getPublicData(9001007).type8List[1];
m_fZhaojiaInc = ConfigDataManager.GetInstance().GetPublicDataConfig().getPublicData(9001007).type8List[2];

m_fZhaojiaDamageAnti = ConfigDataManager.GetInstance().GetPublicDataConfig().getPublicData(9001008).type7Data;
m_fZhaojiaDamageAnti = ConfigDataManager.GetInstance().GetPublicDataConfig().getPublicData(9001009).type7Data;
}

/// <summary>
/// 武器打到角色处理
/// </summary>
/// <param name="attacker">攻击者</param>
/// <param name="target">攻击目标</param>
/// <param name="hitpoint">击中点</param>
public void OnWeaponHitCharacter(Character attacker, Character target, Vector3 hitpoint )
{
SkillCastProcess(attacker.skill.getCurrentSkill(), target, hitpoint);
}

/// <summary>
/// 技能表现处理
/// </summary>
/// <param name="skill">技能表现</param>
/// <param name="target">攻击目标</param>
/// <param name="colliderPos">碰撞点</param>
/// <param name="isJiguan">是否是机关</param>
public void SkillCastProcess(SkillAppear skill, Character target, Vector3 colliderPos, bool isJiguan = false)
{
if (!SkillPreProcess(skill, target))
{
return;
}
}

/// <summary>
/// 技能表现结果
/// </summary>
/// <param name="skill">技能表现</param>
/// <param name="target">攻击目标</param>
public void SkillCastResult(SkillAppear skill, Character target)
{

}

/// <summary>
/// Miss处理
/// </summary>
/// <param name="skill">技能表现</param>
/// <param name="target">攻击目标</param>
public void MissProcess(SkillAppear skill, Character target)
{

}

/// <summary>
/// 技能准备处理
/// </summary>
/// <param name="skill">技能表现</param>
/// <param name="target">攻击目标</param>
public bool SkillPreProcess(SkillAppear skill, Character target)
{

}

// 给怪物添加血条
// 参数,怪物类
public void addHPBarOnMonster(CharacterMonster monster)
{
GameObject phBar;
}

// 设置boss怪物血条
public void setBossMonsterHPBar(CharacterMonster monster)
{

}

/// <summary>
/// 技能区域吞并怪物
/// </summary>
/// <param name="skill">技能表现</param>
/// <param name="monster">怪物</param>
/// <param name="speed">速度</param>
/// <param name="t"></param>
public void onSkillAreaAbsorbMonster(SkillAppear skill, CharacterMonster monster, float speed, float t)
{

}

/// <summary>
/// 主角杀死怪物
/// </summary>
/// <param name="theMonster">怪物控制脚本</param>
public void onPlayerkillMonster(CharacterMonster theMonster)
{

}

// 角色被攻击
public void OnPlayerBeHit()
{


}

// 怪物被攻击
public void OnMonsterBeHit(Character monster)
{

}

/// <summary>
/// 击中处理
/// 默认skillDamage为0 表示不是招架
/// </summary>
/// <param name="skill">技能表现</param>
/// <param name="target">目标</param>
/// <param name="eDamageType">技能结果</param>
/// <param name="targetDamage">目标伤害</param>
/// <param name="skillDamage">技能伤害</param>
public void HitProcess(SkillAppear skill, Character target, COMBAT_SKILL_RESULT eDamageType, float targetDamage, float skillDamage = 0.0f)
{

}

/// <summary>
/// 反击处理
/// </summary>
/// <param name="skill">技能表现</param>
/// <param name="target">目标</param>
public void HitBackFlyBrokenProcess(SkillAppear skill, Character target)
{
target.BeHitBackHitFlyHitBroken(skill);
}

/// <summary>
/// Buff处理
/// </summary>
/// <param name="skill">技能表现</param>
/// <param name="target">目标</param>
public void BuffProcess(SkillAppear skill, Character target)
{

}

/// <summary>
/// 黄金哥布林处理
/// </summary>
/// <param name="skill">技能表现</param>
/// <param name="target">目标</param>
public void GoldenGoblinProcess(SkillAppear skill, Character target)
{

}

/// <summary>
/// 是否命中
/// </summary>
/// <param name="attacker">攻击者</param>
/// <param name="target">目标</param>
bool IsHit(Character attacker, Character target)
{

}

/// <summary>
/// 是否命中
/// </summary>
/// <param name="attacker">攻击者</param>
/// <param name="target">目标</param>
bool IsWithStand(Character attacker, Character target)
{

}

/// <summary>
/// 是不是暴击
/// </summary>
/// <param name="attacker">攻击者</param>
/// <param name="target">目标</param>
bool IsBlastAttack(Character attacker, Character target)
{

}

// 得到暴击时间
float GetBlastTime(Character attacker, Character target)
{

}

// 得到基础攻击
float GetBaseAttack(SkillAppear skill, Character target)
{

}

// 属性触发概率计算公式
// 冰概率
float FrozenChances(Character attacker, Character target)
{

}

// 火概率
float FireChances(Character attacker, Character target)
{
}

// 毒概率
float PoisionChances(Character attacker, Character target)
{
}

// 雷概率
float ThunderChances(Character attacker, Character target)
{
}

// 属性伤害公式
// 冰伤害
float FrozenDamage(Character attacker, Character target)
{
}

// 火伤害
float FireDamage(Character attacker, Character target)
{
}

// 毒伤害
float PoisionDamage(Character attacker, Character target)
{
}

// 雷伤害
float ThunderDamage(Character attacker, Character target)
{

}

// 元素伤害 start
float ElementDamage(Character attacker, Character target)
{

}

public void PlayAudioOneShot()
{

}

// 范围伤害计算
public void RangeSkillDamageCal(SkillAppear skill)
{
}

// 技能影响的对象
// nSkillId,技能ID,从配置获取角度与半径
public List<CharacterMonster> SearchEnemy(int nSkillId)
{

}

// 根据角度和半径获取区域内的怪物
public List<CharacterMonster> GetRangeCharacter(int nDegree, float radius)
{

}

// 得到技能范围的敌人
public Character GetViewRangeEnemy(Character character)
{

}

// 锁定目标
public void ShowCharacterLocked(Character character)
{
Skill_Target_Flag.target = character;
Skill_Target_Flag.playAnimation();
}

public SkillTargetFlag Skill_Target_Flag
{

}

// 汇报打中世界boss消息
public void ReportHitWorldBoss(SkillAppear skill, Character target)
{
}
}

CDManager.cs CD管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
using UnityEngine;
using System.Collections.Generic;

// CD实体,会有很多CD
public class CDObject
{
public float m_fCDTime; // CD时间,例如10秒
public float m_fCurTime; // CD已经进行的时间

public CDObject(float cd)
{
m_fCDTime = cd;
m_fCurTime = 0.0f;
}
}

// 维护管理一个字典,int=>CDObject
public class CDManager
{
public Dictionary<int, CDObject> m_kCDObjContainer;

// 需要移除的
List<int> m_kNeedRemove;

private static CDManager _instance = null;

public static CDManager Instance
{
get
{
if (_instance == null)
_instance = new CDManager();

return _instance;
}
}

private CDManager()
{
m_kNeedRemove = new List<int>();
m_kCDObjContainer = new Dictionary<int, CDObject>();
}

// 是否在CD中
public bool IsInCD(int nTemplateId)
{
if (m_kCDObjContainer.ContainsKey(nTemplateId))
{
return true;
}
return false;
}

public bool IsInCD(SKILL_APPEAR skillAppear)
{
int nConvertId = 0;

switch(skillAppear)
{
case SKILL_APPEAR.SA_MAG_FLASH_AWAY:
nConvertId = 400003001;
break;
}

if (m_kCDObjContainer.ContainsKey(nConvertId))
{
return true;
}
return false;
}

public void AddCDObj(int nTemplateId)
{
if (!IsInCD(nTemplateId))
{
// 从配置读取CD的信息
CDObject cdObj = new CDObject(ConfigDataManager.GetInstance().getSkillConfig().getSkillData(nTemplateId).cool_down * 0.001f);
m_kCDObjContainer.Add(nTemplateId, cdObj);
}
}

void Update()
{
m_kNeedRemove.Clear();

foreach (KeyValuePair<int, CDObject> item in m_kCDObjContainer)
{
item.Value.m_fCurTime += Time.deltaTime;

if (item.Value.m_fCurTime > item.Value.m_fCDTime)
{
m_kNeedRemove.Add(item.Key);
}
}

for (int i = 0; i < m_kNeedRemove.Count; ++i )
{
m_kCDObjContainer.Remove(m_kNeedRemove[i]);
}
}
}

\Manager\MonsterManager.cs 怪兽管理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using MVC.entrance.gate;

public class MonsterManager
{
private static MonsterManager _instance = null;

// 怪兽角色
List<CharacterMonster> m_kMonsterList = null;

// 怪物ID范围 100 - 999
int current_monster_id = 100;

// boss instance id list for camera cruise 相机巡游
public List<int> m_listBossIDInOnePrefab = null;

public static MonsterManager Instance
{
get
{
if(_instance == null)
_instance = new MonsterManager();
return _instance;
}
}

private MonsterManager()
{
m_kMonsterList = new List<CharacterMonster>();
m_listBossIDInOnePrefab = new List<int>();
}

// 生成怪兽
public GameObject spawnMonster(Vector3 pos, Quaternion rot, int templateId, int instanceId = 0)
{
MonsterDataItem data_item = ConfigDataManager.GetInstance().getMonsterConfig().getMonsterData(templateId);

if (data_item == null)
{
Loger.Error(templateId.ToString() + " is not exist!");
return null;
}

GameObject monster = null;
GameObject asset = BundleMemManager.Instance.getPrefabByName(data_item, EBundleType.eBundleMonster);
if (asset != null)
{
monster = BundleMemManager.Instance.instantiateObj(asset, Vector3.zero, rot);
}
else
{
Debug.LogError("not found in monster manager " + data_item.name);
}

if(monster == null)
{
Debug.LogError(data_item.name + " is not exist!");
return null;
}

Collider collider = monster.GetComponent<Collider>();
collider.isTrigger = true;

Rigidbody rb = monster.AddComponent<Rigidbody>();
rb.useGravity = false;
rb.isKinematic = true;

monster.AddComponent("CharacterMonster");
monster.AddComponent<MonsterSkill>();
monster.AddComponent("RenderProperty");
monster.AddComponent<HUD>();
monster.AddComponent<CoolDownProperty>();
monster.AddComponent<AISystem.AIPathFinding>();

CharacterMonster cm = monster.GetComponent<CharacterMonster>();

cm.setFaceDir(rot * Vector3.forward);
cm.skill = monster.GetComponent<Skill>();

if (instanceId == 0)
{
cm.monster_property.setInstanceId(current_monster_id++);
}
else
cm.monster_property.setInstanceId(instanceId);
cm.monster_property.setTemplateId(templateId);
cm.monster_property.setName(data_item.name);
cm.monster_property.strDesName = data_item.desName;
cm.monster_property.setLevel(data_item.level);
cm.monster_property.setType((MonsterProperty.MONSTER_LEVEL_TYPE)data_item.type);
cm.monster_property.SetSurfaceType((MonsterProperty.MONSTER_SURFACE_LIGHT)data_item.surfaceLight);
cm.monster_property.setHP(data_item.hp);
cm.monster_property.setHPMax(data_item.hp);
cm.monster_property.setAttackPower(data_item.attack_power);
cm.monster_property.setAttackRange(data_item.attack_range);
cm.monster_property.setDefence(data_item.defence);
cm.monster_property.setMoveSpeed(data_item.move_speed);
cm.monster_property.attack_interval_upper = data_item.attack_interval_upper;
cm.monster_property.attack_interval_low = data_item.attack_interval_low;
cm.monster_property.setAttackType(data_item.attack_type);
cm.monster_property.setRepelSpeed(data_item.repel_speed);
cm.monster_property.setBroken(data_item.broken);
cm.monster_property.setBrokenPrefab(data_item.broken_prefab);
cm.monster_property.setExp(data_item.exp);
cm.monster_property.setGold(data_item.gold);
cm.monster_property.pszDisplayName = data_item.pszDisplayName;
cm.monster_property.pszDisplayIcon = data_item.pszDisplayIcon;

cm.monster_property.fightProperty.fightData.Add(eFighintPropertyCate.eFPC_Precise, data_item.FPC_Precise);
cm.monster_property.fightProperty.fightData.Add(eFighintPropertyCate.eFPC_Dodge, data_item.FPC_Dodge);
cm.monster_property.fightProperty.fightData.Add(eFighintPropertyCate.eFPC_BlastAttack, data_item.FPC_BlastAttack);
cm.monster_property.fightProperty.fightData.Add(eFighintPropertyCate.eFPC_BlastAttackAdd, data_item.FPC_BlastAttackAdd);

cm.monster_property.fightProperty.fightData.Add(eFighintPropertyCate.eFPC_BlastAttackReduce, data_item.FPC_BlastAttackReduce);
cm.monster_property.fightProperty.fightData.Add(eFighintPropertyCate.eFPC_Tenacity, data_item.FPC_Tenacity);
cm.monster_property.fightProperty.fightData.Add(eFighintPropertyCate.eFPC_FightBreak, data_item.FPC_FightBreak);
cm.monster_property.fightProperty.fightData.Add(eFighintPropertyCate.eFPC_AntiFightBreak, data_item.FPC_AntiFightBreak);

cm.monster_property.fightProperty.fightData.Add(eFighintPropertyCate.eFPC_IceAttack, data_item.FPC_IceAttack);
cm.monster_property.fightProperty.fightData.Add(eFighintPropertyCate.eFPC_AntiIceAttack, data_item.FPC_AntiIceAttack);
cm.monster_property.fightProperty.fightData.Add(eFighintPropertyCate.eFPC_FireAttack, data_item.FPC_FireAttack);
cm.monster_property.fightProperty.fightData.Add(eFighintPropertyCate.eFPC_AntiFireAttack, data_item.FPC_AntiFireAttack);

cm.monster_property.fightProperty.fightData.Add(eFighintPropertyCate.eFPC_PoisonAttack, data_item.FPC_PoisonAttack);
cm.monster_property.fightProperty.fightData.Add(eFighintPropertyCate.eFPC_AntiPoisonAttack, data_item.FPC_AntiPoisonAttack);
cm.monster_property.fightProperty.fightData.Add(eFighintPropertyCate.eFPC_ThunderAttack, data_item.FPC_ThunderAttack);
cm.monster_property.fightProperty.fightData.Add(eFighintPropertyCate.eFPC_AntiThunderAttack, data_item.FPC_AntiThunderAttack);

cm.transform.position = new Vector3(pos.x, pos.y, pos.z);

m_kMonsterList.Add(cm);


}

/// <summary>
/// 得到一定角度内的怪
/// </summary>
/// <param name="nDegree">角度</param>
/// <param name="nDegree">半径</param>
/// <returns>怪物脚本列表</returns>
public List<CharacterMonster> GetAngleRangleMonster(int nDegree, float nDegree)
{
List<CharacterMonster> ret = new List<CharacterMonster>();

if (m_kMonsterList.Count == 0)
{
return null;
}

for (int i = 0; i < m_kMonsterList.Count; i++)
{
CharacterMonster lookMonster = (CharacterMonster)m_kMonsterList[i];

// 死亡状态不算
if (CharacterAI.IsInState(lookMonster, CharacterAI.CHARACTER_STATE.CS_DIE))
continue;

float dist = Vector3.Distance(CharacterPlayer.sPlayerMe.transform.position, lookMonster.transform.position);

// 距离大于半径,不符合条件
if (dist > radius)
continue;

// simply filter backward enemy
// 去掉背后的怪兽
Vector3 posDir = lookMonster.transform.position - CharacterPlayer.sPlayerMe.transform.position;

// Vector3.Dot表示求两个向量的点积;点积计算的结果为数值。点积,可以计算两个向量的夹角
// 通过点积的计算可以简单粗略的判断当前物体是否朝向另外一个物体:只需要计算当前物体的transform.forward向量与(otherObj.transform.position – transform.position)的点积即可,大于0则面对,否则则背对着。

// 可以用点乘来判断物体是在人物的前方,还是后方。
// 用叉乘来判断是在人物的左手边,还是右手边。
// 返回值为正时,目标在自己的右方,反之在自己的左方。
// 点积是一个浮点值,等于两个向量的大小相乘在一起,然后乘以它们之间的夹角的余弦值。
// 返回进行Dot计算的两个向量之间的夹角的余弦值(Cos弧度角)。要注意的是能进行Dot计算的前提是两个向量首先要变成单位向量。
float tmp = Vector3.Dot(posDir, CharacterPlayer.sPlayerMe.getFaceDir());

// 弧长=nπr/180,在这里n就是角度数,即圆心角n所对应的弧长。
// Mathf.Cos 返回x的弧度的余弦值,-1到1之间。
if (tmp < Mathf.Cos(nDegree * 0.5f * Mathf.PI / 180.0f))
continue;

ret.Add(lookMonster);
}
return ret;
}

/// <summary>
/// 得到离某点最近的怪
/// </summary>
/// <param name="pos">一个点</param>
/// <param name="r">距离,例如10米之内寻找最近的</param>
/// <returns>Character对象</returns>
public Character GetPointNearestMonster(Vector3 pos, float r)
{
// float.MaxValue是C#的函数
float fMinDist = float.MaxValue;
int index = -1;

// 一个一个遍历
for (int i = 0; i < m_kMonsterList.Count; i ++)
{
CharacterMonster lookMonster = (CharacterMonster)m_kMonsterList[i];

if (CharacterAI.IsInState(lookMonster, CharacterAI.CHARACTER_STATE.CS_DIE))
continue;

// 怪物如果在出生状态 continue
if (lookMonster.m_eAppearState == Appear.BATTLE_STATE.BS_BORN)
{
continue;
}

Vector3 posDir = lookMonster.getPosition() - pos;

// Vector3.magnitude返回向量的长度(只读)。
// 向量的长度是(x*x+y*y+z*z)的平方根。
float dist = posDir.magnitude;

if (dist < r && dist < fMinDist)
{
// 不断的给fMinDist赋新值
fMinDist = dist;
// index也在不断变化,如果是最小的就不变化了
index = i;
}
}

if (index != -1)
{
return (Character)m_kMonsterList[index];
}

return null;
}

/// <summary>
/// 获取一个怪物相邻的怪物
/// </summary>
/// <param name="m">一个怪物</param>
/// <param name="r">距离</param>
/// <param name="neighbors">相邻的怪物集合</param>
public void getMonsterNeighbors(CharacterMonster m, float r, out ArrayList neighbors)
{
// ArrayList用于创建动态数组,意味着数组的大小根据程序的要求自动增加或减少,不需要指定ArrayList的大小
// ArrayList由于存储的是object类型、在使用的时候进行类型转换、会造成装箱拆箱、从而损耗性能。

    // 装箱:把值类型转换成引用类型;
    // 拆箱:把引用类型转换成值类型。

// 这儿应该是为了在前面加out,所以使用了ArrayList数据结构

ArrayList ns = new ArrayList();

for(int i = 0; i < m_kMonsterList.Count; i++)
{
CharacterMonster monsterComp = (CharacterMonster)m_kMonsterList[i];

if (monsterComp == m)
continue;

Vector3 toNeighbor = monsterComp.getPosition() - m.getPosition();

// Vector3 .sqrMagnitude
// 返回此向量的平方长度(只读)
// 如果需要比较一些向量的长度,可以比较它们长度的平方,使用sqrMagnitude(计算平方很快)。
if (toNeighbor.sqrMagnitude < r * r)
{
ns.Add(monsterComp);
}
}
}
}

Battle

\Battle\Appear.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
using System;
using UnityEngine;
using System.Collections;

// 角色表现
public class Appear
{
// 战斗状态
public enum BATTLE_STATE
{
BS_NULL = 0,
BS_IDLE,
BS_MOVE,
BS_PURSUE, // 追赶表现
BS_BORN,
BS_PING_KAN, // 平砍表现
BS_SKILL, // 技能表现 不包括平砍
BS_BE_HIT, // 受击表现
BS_BE_HIT_BACK, // 反击
BS_BE_HIT_FLY,
BS_BE_HIT_BROKEN,
BS_ROLL, // 翻滚
BS_DIE, // 死亡

BS_DIZZY, // 眩晕
BS_SMOOTH_POS,

BS_GOBLIN_RUN, // 哥布林玩法 跑步表现
}
// 表现是否可用
protected bool is_active = false;

// 表现时长
protected float time_length = 0;
// 已经经过的时间
protected float time_since_begin = 0;
// 表现对应的动画名称
protected string animation_name = "";
// 对象
protected Character owner = null;
// 下一个表现
protected Appear next_inner = null;

protected Appear next = null;

protected BATTLE_STATE battle_state = BATTLE_STATE.BS_NULL;

public virtual void init()
{

}

public virtual void active()
{

}

public virtual void deActive()
{
on_deActive();
}

protected virtual void on_deActive()
{
is_active = false;
}

public virtual void update(float delta)
{
updateTime(delta);
}

protected float updateTime(float delta)
{
// 超时了
if (time_since_begin >= time_length)
{
is_active = false;
return 0;
}
// 将值限制在最小浮动值和最大浮动值之间。
// Clamps the value 10 to be between 1 and 3.
// prints 3 to the console
// print(Mathf.Clamp(10, 1, 3));
float t = Mathf.Clamp(delta, 0, time_length - time_since_begin);

time_since_begin += t;

return t;
}

public void setOwner(Character c)
{
owner = c;
}

public Character getOwner()
{
return owner;
}

public void setNextCmdInner(Appear n)
{

next_inner = n;
}

public Appear getNextCmdInner()
{

return next_inner;
}

public void setNextCmd(Appear n)
{

next = n;
}

public Appear getNextCmd()
{

return next;
}

public virtual bool isActive()
{
if (!is_active) return false;

if (time_since_begin >= time_length) return false;

return true;
}

public BATTLE_STATE GetBattleState()
{
return battle_state;
}

protected virtual bool IsLoopAnimation()
{
return false;
}

protected virtual void on_active(float fAnimTime = 0)
{
time_since_begin = 0;
is_active = true;

// 动画 begin
// 为0使用默认动画长度
if (fAnimTime == 0)
{
time_length = owner.animation[animation_name].length;
}
else
{
time_length = fAnimTime;
}

if (!IsLoopAnimation())
{
time_length /= owner.animation[animation_name].speed;
}

if (animation_name != "")
{
owner.playAnimation(animation_name);
}
// 动画 end

// 设置状态
owner.SetState(battle_state);
}

public void setAnimationTime(float time)
{
owner.setAnimationTime(animation_name, time);
}

public void setAnimationSpeed(float speed)
{
owner.setAnimationSpeed(animation_name, speed);
}
}

JiGuan.cs

1
2


AssetBundleManager

\Code\AssetBundleManager\BundleMemManager.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/**
实现AssetBundle加载的资源内存管理,去除所有的BundleMemManager.Instance.loadResource
**/

// 定义宏来确认是否使用本地Bundle包
// #define USE_LOCAL_BUNDLE

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Object = UnityEngine.Object;

//bundle的类型
public enum EBundleType
{
eBundleCommon = 0, //Common bundle
eBundlePicture, //icon跟场景背景的bundle
eBundleMusic, //音乐的bundle
eBundleWing, //翅膀bundle
eBundleSelectRole, //选角用到的bundle
eBundleScene, //场景的 bundle
eBundleShader, //常驻内存的shader资源
eBundleWeapon, //武器bundle
eBundleWeaponEffect, //武器bundle 特效
eBundleUI, //UI bundle
eBundleUIEffect, //UI bundle 特效
eBundleTaskEffect, //任务bundle 特效
eBundleNPC, //NPC的 bundle
eBundleBattleGoBulin, //哥布林中用到的特殊资源
eBundleBattleEffect, //战斗专用的effect特效
eBundleRoleEffect, //战斗使用的对应角色prefab
eBundleMonster, //怪物bundle
eBundleMulti, //多人副本:战斗bundle
eBundleRaid, //关卡:战斗bundle
eBundleGobulin, //哥布林:战斗bundle
eBundleTower, //爬塔:战斗bundle
eBundleReward, //悬赏战斗 bundle
eBundleScenario, //剧情战斗 bundle
eBundleBoss, //Boss战斗 bundle
eBundlePanduoLa, //潘多拉战斗 bundle
eBundlePet, //宠物模型
eBundleConfig, //配置文件
eBundleOther = 1000, //没有划分类别的bundle,使用Resources.Load
}

public class BundleMemManager
{
private static BundleMemManager _instance = null;
public static bool debugVersion; //使用Debug版本,适用于windows调试
public static bool useLocalServer = true;//发布本地版本

private Dictionary<string, GameObject> _scenePrefabs;//进入场景的所有prefab

private Dictionary<string, List<string>> _prefabsInBundleDic;//根据bundle的名称存储相应bundle下面的所有prefab名称,方便删除scenePrefab

private Dictionary<string, System.Object> _allSceneBundles; //整个场景中用到的Bundle

private Dictionary<string, AssetBundle> _residentBundles; //常驻内存的Bundle
private EBundleType[] _residentTypes; //常驻内存bundle类型

private Dictionary<EBundleType, Dictionary<CHARACTER_CAREER, BundleItem>> _cacheBundleDic;//需要缓存的bundle类型,以bundleType作为key

private Dictionary<string, CHARACTER_CAREER> _preLoadBundleDic; //创角场景用到Bundle集合

public static BundleMemManager Instance
{
get
{
if (_instance == null)
_instance = new BundleMemManager();
return _instance;
}
}


}

\Code\AssetBundleManager\DownloadSceneProgress.cs

1
2


Task

1
2
3
4
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RECORDS>
<RECORD ID="700100001" task_name="[主]希望降临" task_discrip="降临村[00FF00]时光守护者[-]正在找你,与他交谈" task_type="1" task_typeNum="1" nextID="0" start_level="1" finish_level="200" finish_time="0" finish_type="2" finish_Des="与[00FF00]时光守护者基兰[-]交谈" finish_value1="1001002" finish_value2="1" finish_value3="0" start_NPC="1001001" finish_NPC="1001002" start_NPC_word="降临者,你的到来,使灾难有了转机。" start_button_word="领取任务" unfinish_NPC_word="去吧,向[00FF00]雅典娜[-]请教你的神秘状况。" unfinish_button_word="任务进行中" finish_NPC_word="哦?你就是[00FF00]预言中的希望——降临者[-]?" finish_button_word="完成任务" reward_Resource="5,880,1,13" FunctionID="0"/>
</RECORDS>

\Code\UI\Task\Task.cs

1
2


0%